source: petify-frontend/src/views/ClinicApplicationView.vue@ fa32d0f

Last change on this file since fa32d0f was fa32d0f, checked in by veronika-ils <ilioskaveronika@…>, 6 hours ago

Added form for clinics

  • Property mode set to 100644
File size: 7.9 KB
RevLine 
[fa32d0f]1<template>
2 <main class="application-page">
3 <section class="application-hero">
4 <div class="container hero-inner">
5 <div class="hero-copy">
6 <p class="eyebrow">Petify clinics</p>
7 <h1>Apply as a vet clinic</h1>
8 <p>
9 Send your clinic details to the Petify admins. Once approved, your clinic can manage appointments,
10 availability, cancellations, and client visit outcomes from the clinic dashboard.
11 </p>
12 </div>
13 <div class="review-note">
14 <span class="note-label">Admin review</span>
15 <strong>Applications start as pending</strong>
16 <p>Admins can approve or deny them from the Clinics tab.</p>
17 </div>
18 </div>
19 </section>
20
21 <section class="container application-body">
22 <div class="form-shell">
23 <div class="form-heading">
24 <h2>Clinic information</h2>
25 <p>Use details patients can recognize. Email or phone is required.</p>
26 </div>
27
28 <form class="application-form" @submit.prevent="submit">
29 <div class="form-grid">
30 <div class="form-group wide">
31 <label for="clinicName">Clinic name *</label>
32 <input
33 id="clinicName"
34 v-model.trim="form.name"
35 class="form-control"
36 type="text"
37 placeholder="Happy Paws Clinic"
38 required
39 />
40 </div>
41
42 <div class="form-group">
43 <label for="clinicEmail">Email</label>
44 <input
45 id="clinicEmail"
46 v-model.trim="form.email"
47 class="form-control"
48 type="email"
49 placeholder="clinic@example.com"
50 />
51 </div>
52
53 <div class="form-group">
54 <label for="clinicPhone">Phone</label>
55 <input
56 id="clinicPhone"
57 v-model.trim="form.phone"
58 class="form-control"
59 type="tel"
60 placeholder="+389 70 000 000"
61 />
62 </div>
63
64 <div class="form-group">
65 <label for="clinicCity">City *</label>
66 <input
67 id="clinicCity"
68 v-model.trim="form.city"
69 class="form-control"
70 type="text"
71 placeholder="Skopje"
72 required
73 />
74 </div>
75
76 <div class="form-group">
77 <label for="clinicAddress">Address *</label>
78 <input
79 id="clinicAddress"
80 v-model.trim="form.address"
81 class="form-control"
82 type="text"
83 placeholder="Partizanska 12"
84 required
85 />
86 </div>
87 </div>
88
89 <div v-if="errorMessage" class="alert alert-danger">
90 {{ errorMessage }}
91 </div>
92
93 <div v-if="successMessage" class="alert alert-success">
94 {{ successMessage }}
95 </div>
96
97 <div class="form-actions">
98 <button class="btn btn-primary" type="submit" :disabled="isSubmitting">
99 <span v-if="isSubmitting" class="spinner-border spinner-border-sm me-2"></span>
100 {{ isSubmitting ? 'Submitting...' : 'Submit application' }}
101 </button>
102 <button class="btn btn-outline-secondary" type="button" :disabled="isSubmitting" @click="resetForm">
103 Reset
104 </button>
105 </div>
106 </form>
107 </div>
108 </section>
109 </main>
110</template>
111
112<script setup lang="ts">
113import { ref } from 'vue'
114import { submitClinicApplication } from '../api/clinicApplications'
115
116const emptyForm = {
117 name: '',
118 email: '',
119 phone: '',
120 city: '',
121 address: '',
122}
123
124const form = ref({ ...emptyForm })
125const isSubmitting = ref(false)
126const errorMessage = ref('')
127const successMessage = ref('')
128
129function validateForm(): string {
130 if (!form.value.name.trim()) return 'Clinic name is required'
131 if (!form.value.city.trim()) return 'City is required'
132 if (!form.value.address.trim()) return 'Address is required'
133 if (!form.value.email.trim() && !form.value.phone.trim()) return 'Email or phone is required'
134 return ''
135}
136
137async function submit() {
138 const validationError = validateForm()
139 if (validationError) {
140 errorMessage.value = validationError
141 successMessage.value = ''
142 return
143 }
144
145 try {
146 isSubmitting.value = true
147 errorMessage.value = ''
148 successMessage.value = ''
149 const application = await submitClinicApplication(form.value)
150 successMessage.value = `Application submitted. Your status is ${application.status || 'PENDING'}.`
151 form.value = { ...emptyForm }
152 } catch (error) {
153 errorMessage.value = error instanceof Error ? error.message : 'Failed to submit application'
154 } finally {
155 isSubmitting.value = false
156 }
157}
158
159function resetForm() {
160 form.value = { ...emptyForm }
161 errorMessage.value = ''
162 successMessage.value = ''
163}
164</script>
165
166<style scoped>
167.application-page {
168 min-height: calc(100vh - 72px);
169 background: #f7f5f2;
170 color: #1f2933;
171}
172
173.application-hero {
174 background:
175 linear-gradient(135deg, rgba(31, 41, 51, 0.82), rgba(60, 78, 88, 0.68)),
176 url('https://images.unsplash.com/photo-1628009368231-7bb7cfcb0def?auto=format&fit=crop&w=1800&q=80');
177 background-position: center;
178 background-size: cover;
179 color: #ffffff;
180}
181
182.hero-inner {
183 min-height: 360px;
184 display: grid;
185 grid-template-columns: minmax(0, 1fr) 320px;
186 gap: 32px;
187 align-items: end;
188 padding-top: 72px;
189 padding-bottom: 46px;
190}
191
192.hero-copy {
193 max-width: 720px;
194}
195
196.eyebrow {
197 margin: 0 0 10px;
198 font-size: 0.78rem;
199 font-weight: 800;
200 letter-spacing: 0.08em;
201 text-transform: uppercase;
202 color: #fed7aa;
203}
204
205.hero-copy h1 {
206 margin: 0;
207 font-size: clamp(2.35rem, 5vw, 4.7rem);
208 line-height: 0.95;
209 font-weight: 850;
210}
211
212.hero-copy p {
213 margin: 20px 0 0;
214 max-width: 620px;
215 color: rgba(255, 255, 255, 0.9);
216 font-size: 1.04rem;
217 line-height: 1.65;
218}
219
220.review-note {
221 align-self: end;
222 padding: 18px;
223 border: 1px solid rgba(255, 255, 255, 0.24);
224 background: rgba(255, 255, 255, 0.11);
225 backdrop-filter: blur(12px);
226 border-radius: 8px;
227}
228
229.note-label {
230 display: inline-block;
231 margin-bottom: 10px;
232 color: #fed7aa;
233 font-size: 0.78rem;
234 font-weight: 800;
235 text-transform: uppercase;
236}
237
238.review-note strong {
239 display: block;
240 font-size: 1.05rem;
241}
242
243.review-note p {
244 margin: 8px 0 0;
245 color: rgba(255, 255, 255, 0.82);
246}
247
248.application-body {
249 padding-top: 34px;
250 padding-bottom: 64px;
251}
252
253.form-shell {
254 max-width: 940px;
255 margin: 0 auto;
256 background: #ffffff;
257 border: 1px solid #e5e7eb;
258 border-radius: 8px;
259 box-shadow: 0 18px 40px rgba(31, 41, 51, 0.08);
260 padding: 28px;
261}
262
263.form-heading {
264 display: flex;
265 justify-content: space-between;
266 gap: 24px;
267 border-bottom: 1px solid #edf0f2;
268 padding-bottom: 18px;
269 margin-bottom: 22px;
270}
271
272.form-heading h2 {
273 margin: 0;
274 font-size: 1.45rem;
275 font-weight: 800;
276}
277
278.form-heading p {
279 max-width: 340px;
280 margin: 0;
281 color: #667085;
282}
283
284.form-grid {
285 display: grid;
286 grid-template-columns: repeat(2, minmax(0, 1fr));
287 gap: 18px;
288}
289
290.form-group.wide {
291 grid-column: 1 / -1;
292}
293
294.form-group label {
295 display: block;
296 margin-bottom: 7px;
297 color: #344054;
298 font-weight: 700;
299 font-size: 0.92rem;
300}
301
302.form-control {
303 min-height: 46px;
304 border-radius: 8px;
305}
306
307.alert {
308 margin-top: 18px;
309}
310
311.form-actions {
312 display: flex;
313 gap: 12px;
314 margin-top: 22px;
315}
316
317.btn {
318 border-radius: 8px;
319 font-weight: 700;
320}
321
322.btn-primary {
323 background: #f97316;
324 border-color: #f97316;
325}
326
327.btn-primary:hover {
328 background: #ea580c;
329 border-color: #ea580c;
330}
331
332@media (max-width: 768px) {
333 .hero-inner {
334 grid-template-columns: 1fr;
335 min-height: auto;
336 padding-top: 48px;
337 }
338
339 .review-note {
340 max-width: 420px;
341 }
342
343 .form-heading,
344 .form-actions {
345 flex-direction: column;
346 }
347
348 .form-grid {
349 grid-template-columns: 1fr;
350 }
351}
352</style>
Note: See TracBrowser for help on using the repository browser.