Files
windmill/frontend/src/lib/components/ProjectContentBadges.svelte
T
Guilhem LemouelandClaude Opus 5 9c1ef71270 fix: supply the three APIs the import wizard already calls
`AppConnectDrawer`, `ImportProjectStep` and `execution.svelte.ts` landed
calling into props and exports that were never committed alongside them,
so the branch did not type-check. Each half is here now:

- `AppConnectInner.fillPath` — connect into a resource that already
  exists instead of refusing the path. The import creates every resource
  as an empty stub, so without it the connect flow can only ever say
  "already exists, delete it or pick another path". Opt-in: unset, the
  flow still refuses to write over anything, which is what `ResourcePicker`
  and the resources page rely on.
- `ProjectContentBadges.contentSummary` — the badge counts as one line of
  text, for the import step's task row. Shares `kinds()` with the badges
  so a project cannot be counted two ways.
- `installProject.onMigrationsStart` — fires before the reviewed
  migrations run, which is the only signal that phase has begun; the
  import step draws them as their own checklist row off the back of it.

Also fixes the wizard wedging itself shut: `requestClose` set `dismissing`
and cleared it after awaiting the confirmation, so an `ask` that threw left
the flag set — and the backdrop, Escape and the close button all return
early on it, leaving a reload as the only way out. Now `finally`, plus a
reset on open, since a promise that never settles never reaches `finally`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 12:55:19 +02:00

74 lines
2.5 KiB
Svelte

<script lang="ts" module>
export interface ProjectContentCounts {
apps: number
flows: number
scripts: number
resources: number
/** Only the import step counts these. */
triggers?: number
migrations?: number
}
/** The kinds, in the order a project is read. Shared by the badges and the sentence
* below so the same project can never be counted two ways. */
function kinds(counts: ProjectContentCounts) {
return [
{ label: 'app', count: counts.apps },
{ label: 'flow', count: counts.flows },
{ label: 'script', count: counts.scripts },
{ label: 'resource', count: counts.resources },
{ label: 'trigger', count: counts.triggers ?? 0 },
{ label: 'data table migration', count: counts.migrations ?? 0 }
].filter((c) => c.count > 0)
}
/**
* The same counts as one line of text, for callers with a row to sit on rather than
* a space for chips — the import step names them beside the task that imports them.
* Empty when a project has nothing in it, so a caller can drop the whole phrase.
*/
export function contentSummary(counts: ProjectContentCounts): string {
return kinds(counts)
.map((c) => `${c.count} ${c.label}${c.count === 1 ? '' : 's'}`)
.join(', ')
}
</script>
<script lang="ts">
import { Code2, Database, LayoutDashboard, Table2, Zap } from 'lucide-svelte'
import BarsStaggered from '$lib/components/icons/BarsStaggered.svelte'
import Badge from '$lib/components/common/badge/Badge.svelte'
// What a project contains, as one row of badges. Shared by the wizard's project
// card and its import step so the same project never gets counted two ways.
interface Props {
counts: ProjectContentCounts
}
let { counts }: Props = $props()
// Transparent badges throughout: six kinds in six colours turned a summary into
// a paint chart. The icon carries the kind, the colour carries nothing.
//
// Zero counts are dropped rather than shown: a project with no apps should read
// as "no apps", not as a "0 apps" chip the eye has to discount.
const ICONS: Record<string, any> = {
app: LayoutDashboard,
flow: BarsStaggered,
script: Code2,
resource: Database,
trigger: Zap,
'data table migration': Table2
}
const shown = $derived(kinds(counts).map((c) => ({ ...c, icon: ICONS[c.label] })))
</script>
<div class="flex flex-wrap items-center gap-1.5">
{#each shown as c (c.label)}
<Badge color="transparent" small icon={{ icon: c.icon, position: 'left' }}>
{c.count}
{c.label}{c.count === 1 ? '' : 's'}
</Badge>
{/each}
</div>