Implement Fluid Emerald app redesign
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user