Skip to content

Commit b01a8a0

Browse files
committed
update(Frontend v2): Layout
1 parent 1b1b0a6 commit b01a8a0

3 files changed

Lines changed: 145 additions & 31 deletions

File tree

frontend/src/App.vue

Lines changed: 128 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<header class="header">
44
<div class="logo">
55
<h1>FastAPI Forge</h1>
6-
<a class="github-icon" href="https://github.com/mslaursen/fastapi-forge" target="_blank">
6+
<a class="github-icon" href="https://github.com/mslaursen/fastapi-forge" target="_blank" rel="noopener noreferrer">
77
<svg
88
width="20"
99
height="20"
@@ -20,31 +20,64 @@
2020
</svg>
2121
</a>
2222
</div>
23+
<div class="step-wizard">
24+
<div class="step-indicators">
25+
<div class="step-action">
26+
<button :disabled="currentStep <= 0" @click="prevStep" class="btn prev-btn" aria-label="Previous step"><</button>
27+
</div>
28+
<div
29+
v-for="(step, index) in steps"
30+
:key="index"
31+
:class="['step', { active: currentStep === index, completed: currentStep > index }]"
32+
@click="goToStep(index)"
33+
:aria-label="'Go to step ' + (index + 1)"
34+
></div>
35+
<div class="step-action">
36+
<button :disabled="currentStep >= steps.length - 1" @click="nextStep" class="btn next-btn" aria-label="Next step">></button>
37+
</div>
38+
</div>
39+
</div>
2340
</header>
41+
2442
<main class="main">
25-
<div class="content">
26-
<StepWizard :steps="steps" />
43+
<div class="step-content">
44+
<component :is="steps[currentStep]" />
2745
</div>
2846
</main>
47+
48+
<GlobalModal ref="modalRef" />
2949
</div>
30-
<GlobalModal ref="modalRef" />
3150
</template>
3251

3352
<script setup lang="ts">
34-
import StepWizard from "./components/StepWizard.vue"
3553
import ProjectNameStep from "./components/steps/ProjectNameStep.vue"
3654
import DatabaseStep from "./components/steps/DatabaseStep.vue"
3755
import SchemaStep from "./components/steps/SchemaStep.vue"
3856
import FeatureSelectionStep from "./components/steps/FeatureSelectionStep.vue"
3957
import GenerationStep from "./components/steps/GenerationStep.vue"
4058
import GlobalModal from "@/components/modal/GlobalModal.vue"
41-
42-
import type { Component } from "vue"
59+
import { ref, type Component } from "vue"
4360
4461
const steps: Component[] = [ProjectNameStep, DatabaseStep, SchemaStep, FeatureSelectionStep, GenerationStep]
62+
const currentStep = ref(0)
63+
const goToStep = (index: number) => {
64+
if (index >= 0 && index < steps.length) {
65+
currentStep.value = index
66+
}
67+
}
68+
const nextStep = () => {
69+
if (currentStep.value < steps.length - 1) {
70+
currentStep.value++
71+
}
72+
}
73+
const prevStep = () => {
74+
if (currentStep.value > 0) {
75+
currentStep.value--
76+
}
77+
}
4578
</script>
4679

47-
<style>
80+
<style >
4881
html,
4982
body {
5083
height: 100%;
@@ -58,27 +91,41 @@ body {
5891
background-color: var(--color-background);
5992
}
6093
94+
.step-content {
95+
margin-bottom: 3rem;
96+
display: flex;
97+
justify-content: center;
98+
}
99+
61100
.header {
62101
height: 60px;
63102
position: sticky;
64103
flex-shrink: 0;
65104
background-color: #ffffff;
66105
border-bottom: 4px solid black;
67-
top: 0;
68-
display: flex;
69-
align-items: center;
70-
}
71-
72-
.content {
73-
flex-grow: 1;
74-
padding: 1rem;
106+
display: grid;
107+
grid-template-columns: 1fr auto 1fr;
108+
padding: 0 1rem;
75109
}
76110
77111
.logo {
78112
display: flex;
79113
align-items: center;
80114
gap: 0.5rem;
81-
margin-left: 1rem;
115+
grid-column: 1;
116+
height: 60px;
117+
}
118+
119+
.step-wizard {
120+
grid-column: 2;
121+
display: flex;
122+
justify-content: center;
123+
align-items: center;
124+
}
125+
126+
.main {
127+
flex-grow: 1;
128+
padding: 1rem;
82129
}
83130
84131
.github-icon {
@@ -104,4 +151,67 @@ body {
104151
.github-icon:focus {
105152
color: black;
106153
}
107-
</style>
154+
155+
.step-indicators {
156+
display: flex;
157+
justify-content: center;
158+
align-items: center;
159+
gap: 1rem;
160+
}
161+
162+
.step {
163+
caret-color: transparent;
164+
width: 50px;
165+
height: 50px;
166+
border-radius: 50%;
167+
background-color: lightgray;
168+
border: 2px solid black;
169+
box-shadow: 2px 2px 0px rgba(0, 0, 0, 1);
170+
transition:
171+
transform 0.1s ease-out,
172+
box-shadow 0.1s;
173+
}
174+
175+
.step:hover {
176+
background-color: darkgray;
177+
box-shadow: 0px 0px 0px rgba(0, 0, 0, 1);
178+
transform: translate(2px, 2px);
179+
cursor: pointer;
180+
}
181+
182+
.step.active {
183+
background-color: var(--color-primary);
184+
}
185+
186+
.step.completed {
187+
background-color: var(--color-success);
188+
}
189+
190+
.step-action {
191+
display: flex;
192+
justify-content: center;
193+
}
194+
195+
.next-btn,
196+
.prev-btn,
197+
.finish-btn {
198+
caret-color: transparent;
199+
padding: 0.5rem 1rem;
200+
border: 2px solid black;
201+
border-radius: 4px;
202+
background-color: var(--color-background);
203+
box-shadow: 3px 3px 0px rgba(0, 0, 0, 1);
204+
transition:
205+
transform 0.1s ease-in-out,
206+
box-shadow 0.1s;
207+
font-weight: bold;
208+
}
209+
210+
.next-btn:hover,
211+
.prev-btn:hover,
212+
.finish-btn:hover {
213+
transform: translate(2px, 2px);
214+
box-shadow: 0px 0px 0px rgba(0, 0, 0, 1);
215+
cursor: pointer;
216+
}
217+
</style>

frontend/src/components/StepWizard.vue

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="StepWizard">
2+
<div class="step-wizard">
33
<div class="step-indicators">
44
<div
55
v-for="(step, index) in steps"
@@ -9,9 +9,9 @@
99
></div>
1010
</div>
1111

12-
<div class="step-content">
12+
<!-- <div class="step-content">
1313
<component :is="steps[currentStep]" />
14-
</div>
14+
</div> -->
1515

1616
<div class="step-actions">
1717
<button v-if="currentStep > 0" @click="currentStep--" class="btn prev-btn">Previous</button>
@@ -28,33 +28,36 @@ import { ref } from "vue"
2828
const props = defineProps({
2929
steps: {
3030
type: Array,
31-
3231
required: true,
3332
},
3433
})
3534
3635
const currentStep = ref(0)
36+
const emit = defineEmits(["select-step"])
37+
38+
39+
const onStepSelected = (index: number) => {
40+
emit("select-step", index)
41+
}
3742
3843
const nextStep = () => {
3944
if (currentStep.value < props.steps.length - 1) {
4045
currentStep.value++
46+
onStepSelected(currentStep.value)
4147
}
4248
}
4349
44-
const goToStep = (index) => {
50+
const goToStep = (index: number) => {
4551
if (index >= 0 && index < props.steps.length) {
4652
currentStep.value = index
53+
onStepSelected(currentStep.value)
4754
}
4855
}
56+
57+
4958
</script>
5059

5160
<style scoped>
52-
.StepWizard {
53-
display: flex;
54-
flex-direction: column;
55-
align-items: center;
56-
height: 100%;
57-
}
5861
5962
.step-indicators {
6063
display: flex;
@@ -139,3 +142,4 @@ const goToStep = (index) => {
139142
cursor: pointer;
140143
}
141144
</style>
145+

frontend/src/components/ValidatedInput.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ interface Props {
2121
modelValue: string
2222
label?: string
2323
validate?: ((value: string) => boolean) | ((value: string) => string | null)
24-
errorMessage?: string // used if validate returns boolean
24+
errorMessage?: string
2525
}
2626
2727
const props = defineProps<Props>()
@@ -40,7 +40,7 @@ const runValidation = (value: string) => {
4040
if (typeof result === "boolean") {
4141
error.value = result ? null : (props.errorMessage ?? "Invalid value")
4242
} else {
43-
error.value = result // already string|null from custom function
43+
error.value = result
4444
}
4545
}
4646

0 commit comments

Comments
 (0)