profileShare

rasmusjy / splitapp-frontend-vue

Read-only snapshot

No repository description.

main default branch 96 files Expires Sep 13, 2026, 9:06 AM
EditView.vue 6,065 bytes
1 <script setup lang="ts">
2 import { ref, computed, onMounted } from 'vue'
3 import { useRouter, useRoute } from 'vue-router'
4 import { useI18n } from 'vue-i18n'
5 import TripService from '@/services/TripService'
6 import CurrencyService from '@/services/CurrencyService'
7 import type { ICurrency } from '@/types/ICurrency'
8
9 const router = useRouter()
10 const route = useRoute()
11 const { t } = useI18n()
12
13 const name = ref('')
14 const description = ref('')
15 const destination = ref('')
16 const startDate = ref('')
17 const endDate = ref('')
18 const defaultCurrencyId = ref('')
19 const status = ref('Active')
20 const currencies = ref<ICurrency[]>([])
21 const errors = ref<string[]>([])
22 const isSaving = ref(false)
23 const isLoading = ref(true)
24
25 const dateError = computed<string | null>(() => {
26 if (!startDate.value || !endDate.value) return null
27 return endDate.value < startDate.value
28 ? t('trips.create.dateError')
29 : null
30 })
31
32 const tripId = route.params.tripId as string
33
34 onMounted(async () => {
35 const [tripResult, currencyResult] = await Promise.all([
36 TripService.getById(tripId),
37 CurrencyService.getAll(),
38 ])
39
40 if (currencyResult.data) {
41 currencies.value = currencyResult.data
42 }
43
44 if (tripResult.data) {
45 name.value = tripResult.data.name
46 description.value = tripResult.data.description ?? ''
47 destination.value = tripResult.data.destination ?? ''
48 startDate.value = tripResult.data.startDate?.substring(0, 10) ?? ''
49 endDate.value = tripResult.data.endDate?.substring(0, 10) ?? ''
50 defaultCurrencyId.value = tripResult.data.defaultCurrencyId
51 status.value = tripResult.data.status
52 } else {
53 errors.value = tripResult.errors ?? [t('trips.edit.notFound')]
54 }
55 isLoading.value = false
56 })
57
58 async function handleSubmit() {
59 if (dateError.value) {
60 errors.value = [dateError.value]
61 return
62 }
63 errors.value = []
64 isSaving.value = true
65
66 const result = await TripService.update(tripId, {
67 id: tripId,
68 name: name.value,
69 description: description.value || null,
70 destination: destination.value || null,
71 startDate: startDate.value ? new Date(startDate.value).toISOString() : null,
72 endDate: endDate.value ? new Date(endDate.value).toISOString() : null,
73 defaultCurrencyId: defaultCurrencyId.value,
74 status: status.value,
75 })
76
77 if (result.errors) {
78 errors.value = result.errors
79 } else {
80 router.push({ name: 'TripsIndex' })
81 }
82
83 isSaving.value = false
84 }
85
86 async function handleDelete() {
87 if (!confirm(t('trips.edit.confirmDelete'))) return
88 const result = await TripService.delete(tripId)
89 if (result.errors) {
90 errors.value = result.errors
91 } else {
92 router.push({ name: 'TripsIndex' })
93 }
94 }
95 </script>
96
97 <template>
98 <div class="row justify-content-center">
99 <div v-if="isLoading" class="text-center py-5">
100 <div class="spinner-border" role="status"></div>
101 </div>
102
103 <div v-else class="col-md-8 col-lg-6">
104 <h2>{{ t('trips.edit.title') }}</h2>
105
106 <div v-if="errors.length" class="alert alert-danger">
107 <div v-for="error in errors" :key="error">{{ error }}</div>
108 </div>
109
110 <form @submit.prevent="handleSubmit">
111 <div class="mb-3">
112 <label for="name" class="form-label">{{ t('trips.edit.name') }}</label>
113 <input id="name" v-model="name" type="text" class="form-control" required />
114 </div>
115
116 <div class="mb-3">
117 <label for="destination" class="form-label">{{ t('trips.edit.destination') }}</label>
118 <input id="destination" v-model="destination" type="text" class="form-control" />
119 </div>
120
121 <div class="mb-3">
122 <label for="description" class="form-label">{{ t('trips.edit.description') }}</label>
123 <textarea id="description" v-model="description" class="form-control" rows="3"></textarea>
124 </div>
125
126 <div class="row mb-3">
127 <div class="col-6">
128 <label for="startDate" class="form-label">{{ t('trips.edit.startDate') }}</label>
129 <input id="startDate" v-model="startDate" type="date" class="form-control" />
130 </div>
131 <div class="col-6">
132 <label for="endDate" class="form-label">{{ t('trips.edit.endDate') }}</label>
133 <input
134 id="endDate"
135 v-model="endDate"
136 type="date"
137 class="form-control"
138 :class="{ 'is-invalid': dateError }"
139 :min="startDate || undefined"
140 />
141 <div v-if="dateError" class="invalid-feedback">{{ dateError }}</div>
142 </div>
143 </div>
144
145 <div class="mb-3">
146 <label for="currency" class="form-label">{{ t('trips.edit.currency') }}</label>
147 <select id="currency" v-model="defaultCurrencyId" class="form-select" required>
148 <option value="" disabled>{{ t('trips.edit.selectCurrency') }}</option>
149 <option v-for="c in currencies" :key="c.id" :value="c.id">
150 {{ c.code }} — {{ c.name }} ({{ c.symbol }})
151 </option>
152 </select>
153 </div>
154
155 <div class="mb-3">
156 <label for="status" class="form-label">{{ t('trips.edit.status') }}</label>
157 <select id="status" v-model="status" class="form-select">
158 <option value="Active">{{ t('trips.status.Active') }}</option>
159 <option value="Completed">{{ t('trips.status.Completed') }}</option>
160 </select>
161 </div>
162
163 <div class="d-flex gap-2">
164 <button type="button" class="btn btn-danger" @click="handleDelete">{{ t('common.delete') }}</button>
165 <div class="flex-grow-1"></div>
166 <button
167 type="button"
168 class="btn btn-outline-secondary"
169 @click="router.push({ name: 'TripsIndex' })"
170 >
171 {{ t('common.cancel') }}
172 </button>
173 <button type="submit" class="btn btn-primary" :disabled="isSaving || !!dateError">
174 {{ isSaving ? t('common.saving') : t('common.saveChanges') }}
175 </button>
176 </div>
177 </form>
178 </div>
179 </div>
180 </template>
181