mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-20 15:22:59 +00:00
chore: merge main into fix/ui-polish, resolve StackRow conflict
This commit is contained in:
@@ -40,6 +40,7 @@ interface StackAnatomyPanelProps {
|
||||
}
|
||||
|
||||
type SemverBump = 'none' | 'patch' | 'minor' | 'major' | 'unknown';
|
||||
type UpdateKind = 'tag' | 'digest' | 'none';
|
||||
|
||||
interface UpdatePreviewSummary {
|
||||
has_update: boolean;
|
||||
@@ -47,12 +48,16 @@ interface UpdatePreviewSummary {
|
||||
current_tag: string | null;
|
||||
next_tag: string | null;
|
||||
semver_bump: SemverBump;
|
||||
update_kind?: UpdateKind;
|
||||
blocked: boolean;
|
||||
blocked_reason: string | null;
|
||||
has_build_services: boolean;
|
||||
rebuild_available: boolean;
|
||||
}
|
||||
|
||||
interface UpdatePreview {
|
||||
summary: UpdatePreviewSummary;
|
||||
build_services?: string[];
|
||||
changelog: string | null;
|
||||
}
|
||||
|
||||
@@ -143,8 +148,9 @@ export default function StackAnatomyPanel({
|
||||
if (cancelled || !res.ok) return;
|
||||
const data = await res.json();
|
||||
if (!cancelled) {
|
||||
setPreflightSeverity(typeof data?.highestSeverity === 'string' ? data.highestSeverity : null);
|
||||
setPreflightFindings(Array.isArray(data?.findings) ? data.findings : undefined);
|
||||
setPreflightSeverity(typeof data?.activeHighestSeverity === 'string' ? data.activeHighestSeverity : null);
|
||||
const findings = Array.isArray(data?.findings) ? data.findings : undefined;
|
||||
setPreflightFindings(findings?.filter((f: { acknowledged?: boolean }) => !f.acknowledged));
|
||||
}
|
||||
} catch {
|
||||
if (!cancelled) { setPreflightSeverity(null); setPreflightFindings(undefined); }
|
||||
@@ -337,6 +343,10 @@ export default function StackAnatomyPanel({
|
||||
|
||||
const bump = updatePreview?.summary.semver_bump ?? 'none';
|
||||
const hasUpdate = Boolean(updatePreview?.summary.has_update);
|
||||
const hasBuildServices = Boolean(updatePreview?.summary.has_build_services);
|
||||
const rebuildAvailable = Boolean(updatePreview?.summary.rebuild_available);
|
||||
const showUpdateBanner = hasUpdate || rebuildAvailable;
|
||||
const updateKind = updatePreview?.summary.update_kind ?? 'none';
|
||||
const blocked = Boolean(updatePreview?.summary.blocked);
|
||||
const bannerSeverity: 'danger' | 'warn' | 'ok' = bump === 'major' || blocked
|
||||
? 'danger'
|
||||
@@ -354,13 +364,29 @@ export default function StackAnatomyPanel({
|
||||
const bumpLabel = bump === 'none' || bump === 'unknown' ? '' : `${bump}`;
|
||||
const bannerLeadIn = blocked
|
||||
? 'review required'
|
||||
: bump === 'patch'
|
||||
? 'safe to apply'
|
||||
: bump === 'minor'
|
||||
? 'review recommended'
|
||||
: bump === 'major'
|
||||
? 'breaking changes possible'
|
||||
: '';
|
||||
: hasUpdate && updateKind === 'digest'
|
||||
? 'same-tag digest rebuild'
|
||||
: hasUpdate && hasBuildServices
|
||||
? 'registry update + local rebuild'
|
||||
: rebuildAvailable && !hasUpdate
|
||||
? 'local build / rebuild required'
|
||||
: bump === 'patch'
|
||||
? 'safe to apply'
|
||||
: bump === 'minor'
|
||||
? 'review recommended'
|
||||
: bump === 'major'
|
||||
? 'breaking changes possible'
|
||||
: '';
|
||||
const buildServiceNames = updatePreview?.build_services ?? [];
|
||||
const buildHint = hasBuildServices
|
||||
? `Rebuilds ${buildServiceNames.length} local build service${buildServiceNames.length === 1 ? '' : 's'} from Dockerfile context; may take longer and needs network access for base images.`
|
||||
: '';
|
||||
const gitRebuildHint = hasBuildServices && activeGitSource
|
||||
? 'After applying Git source changes, use Rebuild & Update to deploy the updated source.'
|
||||
: '';
|
||||
const applyLabel = hasBuildServices
|
||||
? (applying ? 'rebuilding...' : 'Rebuild & Update')
|
||||
: (applying ? 'applying...' : 'apply');
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col rounded-xl border border-muted bg-card/40">
|
||||
@@ -531,13 +557,13 @@ export default function StackAnatomyPanel({
|
||||
</Row>
|
||||
</>
|
||||
)}
|
||||
{hasUpdate && updatePreview && (
|
||||
{showUpdateBanner && updatePreview && (
|
||||
<div data-testid="update-available-banner" className={cn('mt-3 mb-3 rounded-lg border p-3', bannerTone)}>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="min-w-0">
|
||||
<div className="font-mono text-xs uppercase tracking-wide">
|
||||
Update available
|
||||
{updatePreview.summary.current_tag && updatePreview.summary.next_tag && (
|
||||
{hasBuildServices && !hasUpdate ? 'Rebuild available' : 'Update available'}
|
||||
{updatePreview.summary.current_tag && updatePreview.summary.next_tag && hasUpdate && (
|
||||
<span className="text-foreground">
|
||||
{' · '}
|
||||
<span className="text-stat-subtitle">{updatePreview.summary.current_tag}</span>
|
||||
@@ -550,6 +576,8 @@ export default function StackAnatomyPanel({
|
||||
{[
|
||||
bumpLabel,
|
||||
bannerLeadIn,
|
||||
buildHint,
|
||||
gitRebuildHint,
|
||||
updatePreview.changelog ? updatePreview.changelog.split(/[.\n]/)[0] : '',
|
||||
].filter(Boolean).join(' · ')}
|
||||
</div>
|
||||
@@ -567,7 +595,7 @@ export default function StackAnatomyPanel({
|
||||
onClick={onApplyUpdate}
|
||||
>
|
||||
<Rocket className={cn('h-3 w-3', applying && 'animate-pulse')} strokeWidth={1.5} />
|
||||
{applying ? 'applying...' : 'apply'}
|
||||
{applyLabel}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -646,7 +674,7 @@ export default function StackAnatomyPanel({
|
||||
)}
|
||||
{doctorEnabled && (
|
||||
<TabsContent value="doctor" className="flex flex-col flex-1 min-h-0 mt-0">
|
||||
<PreflightPanel stackName={stackName} />
|
||||
<PreflightPanel stackName={stackName} canEdit={canEdit} />
|
||||
</TabsContent>
|
||||
)}
|
||||
{storageEnabled && (
|
||||
|
||||
Reference in New Issue
Block a user