Implement Fluid Emerald app redesign
linter / quality (pull_request) Successful in 1m18s
tests / ci (8.4) (pull_request) Successful in 1m54s

This commit is contained in:
2026-07-18 16:45:54 +02:00
parent 59817ad30f
commit a46695c2eb
62 changed files with 2165 additions and 1434 deletions
+91 -34
View File
@@ -31,15 +31,19 @@ const breadcrumbs: BreadcrumbItem[] = [
},
];
const defaultTemplateId = props.selectedSourceTemplateId ?? props.sourceTemplates[0]?.id ?? null;
const defaultTemplateId =
props.selectedSourceTemplateId ?? props.sourceTemplates[0]?.id ?? null;
const form = useForm({
copy_areas: [] as string[],
creation_mode: defaultTemplateId !== null ? 'template' : 'scratch',
key: '',
name: '',
source_project_id: props.sourceProjects[0]?.id ? String(props.sourceProjects[0].id) : '',
source_template_id: defaultTemplateId !== null ? String(defaultTemplateId) : '',
source_project_id: props.sourceProjects[0]?.id
? String(props.sourceProjects[0].id)
: '',
source_template_id:
defaultTemplateId !== null ? String(defaultTemplateId) : '',
});
const hasSourceProjects = computed(() => props.sourceProjects.length > 0);
@@ -48,7 +52,9 @@ const hasSourceTemplates = computed(() => props.sourceTemplates.length > 0);
const copyAreasError = computed(() => {
return (
form.errors.copy_areas ??
Object.entries(form.errors).find(([key]) => key.startsWith('copy_areas.'))?.[1] ??
Object.entries(form.errors).find(([key]) =>
key.startsWith('copy_areas.'),
)?.[1] ??
''
);
});
@@ -73,7 +79,7 @@ const submit = (): void => {
<AppLayout :breadcrumbs="breadcrumbs">
<Head title="New project" />
<div class="mx-auto flex w-full max-w-3xl flex-1 flex-col gap-6 p-4">
<div class="app-page-narrow max-w-3xl">
<Heading
title="Create project"
description="Create a fresh project, duplicate selected settings from an existing project, or start from a saved template."
@@ -93,7 +99,9 @@ const submit = (): void => {
<div class="grid gap-3">
<Label>How do you want to start?</Label>
<div class="grid gap-3 sm:grid-cols-3">
<Label class="flex cursor-pointer items-start gap-3 rounded-xl border p-4">
<Label
class="flex cursor-pointer items-start gap-3 rounded-xl border p-4"
>
<input
v-model="form.creation_mode"
type="radio"
@@ -101,16 +109,25 @@ const submit = (): void => {
class="mt-1"
/>
<div class="space-y-1">
<div class="font-medium">Create from scratch</div>
<p class="text-muted-foreground text-sm">
Start with the default project setup.
<div class="font-medium">
Create from scratch
</div>
<p
class="text-muted-foreground text-sm"
>
Start with the default project
setup.
</p>
</div>
</Label>
<Label
class="flex items-start gap-3 rounded-xl border p-4"
:class="hasSourceProjects ? 'cursor-pointer' : 'cursor-not-allowed opacity-60'"
:class="
hasSourceProjects
? 'cursor-pointer'
: 'cursor-not-allowed opacity-60'
"
>
<input
v-model="form.creation_mode"
@@ -120,16 +137,26 @@ const submit = (): void => {
:disabled="!hasSourceProjects"
/>
<div class="space-y-1">
<div class="font-medium">Create from existing project</div>
<p class="text-muted-foreground text-sm">
Copy only the settings you explicitly select. Tickets are never duplicated.
<div class="font-medium">
Create from existing project
</div>
<p
class="text-muted-foreground text-sm"
>
Copy only the settings you
explicitly select. Tickets are never
duplicated.
</p>
</div>
</Label>
<Label
class="flex items-start gap-3 rounded-xl border p-4"
:class="hasSourceTemplates ? 'cursor-pointer' : 'cursor-not-allowed opacity-60'"
:class="
hasSourceTemplates
? 'cursor-pointer'
: 'cursor-not-allowed opacity-60'
"
>
<input
v-model="form.creation_mode"
@@ -139,9 +166,14 @@ const submit = (): void => {
:disabled="!hasSourceTemplates"
/>
<div class="space-y-1">
<div class="font-medium">Create from template</div>
<p class="text-muted-foreground text-sm">
Reuse a saved configuration template.
<div class="font-medium">
Create from template
</div>
<p
class="text-muted-foreground text-sm"
>
Reuse a saved configuration
template.
</p>
</div>
</Label>
@@ -172,7 +204,9 @@ const submit = (): void => {
/>
<p class="text-muted-foreground text-sm">
Example ticket keys will look like
<span class="text-foreground font-medium">CORE-1</span>.
<span class="text-foreground font-medium"
>CORE-1</span
>.
</p>
<InputError :message="form.errors.key" />
</div>
@@ -182,33 +216,43 @@ const submit = (): void => {
class="space-y-6 rounded-xl border border-dashed p-4"
>
<div class="grid gap-2">
<Label for="source_project_id">Source project</Label>
<Label for="source_project_id"
>Source project</Label
>
<select
id="source_project_id"
v-model="form.source_project_id"
class="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
class="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
:disabled="!hasSourceProjects"
>
<option value="">Select a source project</option>
<option value="">
Select a source project
</option>
<option
v-for="sourceProject in sourceProjects"
:key="sourceProject.id"
:value="String(sourceProject.id)"
>
{{ sourceProject.name }} ({{ sourceProject.key }})
{{ sourceProject.name }} ({{
sourceProject.key
}})
</option>
</select>
<p class="text-muted-foreground text-sm">
The new project keeps its own identity, key, and future ticket numbering.
The new project keeps its own identity, key,
and future ticket numbering.
</p>
<InputError :message="form.errors.source_project_id" />
<InputError
:message="form.errors.source_project_id"
/>
</div>
<div class="grid gap-3">
<div>
<Label>Copy options</Label>
<p class="text-muted-foreground text-sm">
Choose which settings should be copied into the new project.
Choose which settings should be copied
into the new project.
</p>
</div>
@@ -225,8 +269,12 @@ const submit = (): void => {
class="mt-1"
/>
<div class="space-y-1">
<div class="font-medium">{{ copyAreaOption.label }}</div>
<p class="text-muted-foreground text-sm">
<div class="font-medium">
{{ copyAreaOption.label }}
</div>
<p
class="text-muted-foreground text-sm"
>
{{ copyAreaOption.description }}
</p>
</div>
@@ -234,7 +282,8 @@ const submit = (): void => {
</div>
<p class="text-muted-foreground text-sm">
Operational ticket data is always left behind so the new project starts clean.
Operational ticket data is always left
behind so the new project starts clean.
</p>
<InputError :message="copyAreasError" />
</div>
@@ -249,7 +298,7 @@ const submit = (): void => {
<select
id="source_template_id"
v-model="form.source_template_id"
class="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
class="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
:disabled="!hasSourceTemplates"
>
<option value="">Select a template</option>
@@ -258,21 +307,29 @@ const submit = (): void => {
:key="sourceTemplate.id"
:value="String(sourceTemplate.id)"
>
{{ sourceTemplate.name }} ({{ sourceTemplate.slug }})
{{ sourceTemplate.name }} ({{
sourceTemplate.slug
}})
</option>
</select>
<p class="text-muted-foreground text-sm">
Templates apply their saved configuration groups to the new project.
Templates apply their saved configuration
groups to the new project.
</p>
<InputError :message="form.errors.source_template_id" />
<InputError
:message="form.errors.source_template_id"
/>
</div>
</div>
<div
v-else-if="!hasSourceProjects && !hasSourceTemplates"
v-else-if="
!hasSourceProjects && !hasSourceTemplates
"
class="text-muted-foreground text-sm"
>
No accessible source projects or saved templates are available yet.
No accessible source projects or saved templates are
available yet.
</div>
<div class="flex items-center gap-3">