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 7,099 bytes
1 <script setup lang="ts">
2 import { ref, onMounted } from 'vue'
3 import { useRouter, useRoute } from 'vue-router'
4 import { useI18n } from 'vue-i18n'
5 import WishlistService from '@/services/WishlistService'
6
7 const router = useRouter()
8 const route = useRoute()
9 const { t } = useI18n()
10
11 const tripId = route.params.tripId as string
12 const itemId = route.params.id as string
13
14 const title = ref('')
15 const description = ref('')
16 const category = ref('Place')
17 const priority = ref('NiceToHave')
18 const estimatedCost = ref<number | null>(null)
19 const url = ref('')
20 const location = ref('')
21 const errors = ref<string[]>([])
22 const isSaving = ref(false)
23 const isLoading = ref(true)
24
25 onMounted(async () => {
26 const result = await WishlistService.getByTrip(tripId)
27 if (result.data) {
28 const item = result.data.find((i) => i.id === itemId)
29 if (item) {
30 title.value = item.title
31 description.value = item.description ?? ''
32 category.value = item.category
33 priority.value = item.priority
34 estimatedCost.value = item.estimatedCost
35 url.value = item.url ?? ''
36 location.value = item.location ?? ''
37 } else {
38 errors.value = [t('wishlist.edit.notFound')]
39 }
40 } else if (result.errors) {
41 errors.value = result.errors
42 }
43 isLoading.value = false
44 })
45
46 async function handleSubmit() {
47 errors.value = []
48 isSaving.value = true
49
50 const result = await WishlistService.update(itemId, {
51 tripId,
52 title: title.value,
53 description: description.value || null,
54 category: category.value,
55 priority: priority.value,
56 estimatedCost: estimatedCost.value,
57 url: url.value || null,
58 location: location.value || null,
59 })
60
61 if (result.errors) {
62 errors.value = result.errors
63 } else {
64 router.push({ name: 'WishlistIndex', params: { tripId } })
65 }
66
67 isSaving.value = false
68 }
69
70 async function handleDelete() {
71 if (!confirm(t('wishlist.edit.confirmDelete'))) return
72 const result = await WishlistService.delete(itemId)
73 if (result.errors) {
74 errors.value = result.errors
75 } else {
76 router.push({ name: 'WishlistIndex', params: { tripId } })
77 }
78 }
79 </script>
80
81 <template>
82 <div class="row justify-content-center">
83 <!-- Loading -->
84 <div v-if="isLoading" class="text-center py-5">
85 <div class="spinner-border" style="color: var(--sa-primary)" role="status"></div>
86 </div>
87
88 <div v-else class="col-md-8 col-lg-6">
89 <h4 class="mb-4" style="color: var(--sa-gray-900)">
90 <i class="bi bi-pencil-square me-2" style="color: var(--sa-accent)"></i>{{ t('wishlist.edit.title') }}
91 </h4>
92
93 <!-- Error Display -->
94 <div v-if="errors.length" class="sa-card-static sa-card-accent sa-card-accent-danger mb-4">
95 <div class="sa-card-body">
96 <div class="d-flex align-items-center mb-1">
97 <i class="bi bi-exclamation-triangle-fill me-2" style="color: var(--sa-danger)"></i>
98 <strong style="color: var(--sa-danger)">{{ t('wishlist.edit.somethingWrong') }}</strong>
99 </div>
100 <div v-for="err in errors" :key="err" style="font-size: 0.875rem; color: var(--sa-gray-600)">
101 {{ err }}
102 </div>
103 </div>
104 </div>
105
106 <div class="sa-card-static">
107 <div class="sa-card-body">
108 <form @submit.prevent="handleSubmit">
109 <div class="mb-3">
110 <label for="title" class="form-label">{{ t('wishlist.create.itemTitle') }}</label>
111 <input id="title" v-model="title" type="text" class="form-control" required :placeholder="t('wishlist.create.titlePlaceholder')" />
112 </div>
113
114 <div class="mb-3">
115 <label for="description" class="form-label">{{ t('wishlist.create.description') }}</label>
116 <textarea id="description" v-model="description" class="form-control" rows="3" :placeholder="t('wishlist.create.descriptionPlaceholder')"></textarea>
117 </div>
118
119 <div class="row mb-3">
120 <div class="col-6">
121 <label for="category" class="form-label">{{ t('wishlist.create.category') }}</label>
122 <select id="category" v-model="category" class="form-select">
123 <option value="Place">{{ t('wishlist.category.Place') }}</option>
124 <option value="Activity">{{ t('wishlist.category.Activity') }}</option>
125 <option value="Restaurant">{{ t('wishlist.category.Restaurant') }}</option>
126 <option value="Other">{{ t('wishlist.category.Other') }}</option>
127 </select>
128 </div>
129 <div class="col-6">
130 <label for="priority" class="form-label">{{ t('wishlist.create.priority') }}</label>
131 <select id="priority" v-model="priority" class="form-select">
132 <option value="MustDo">{{ t('wishlist.priority.MustDo') }}</option>
133 <option value="NiceToHave">{{ t('wishlist.priority.NiceToHave') }}</option>
134 <option value="Optional">{{ t('wishlist.priority.Optional') }}</option>
135 </select>
136 </div>
137 </div>
138
139 <div class="mb-3">
140 <label for="estimatedCost" class="form-label">{{ t('wishlist.create.estimatedCost') }}</label>
141 <input
142 id="estimatedCost"
143 v-model.number="estimatedCost"
144 type="number"
145 step="0.01"
146 min="0"
147 class="form-control"
148 placeholder="0.00"
149 />
150 </div>
151
152 <div class="mb-3">
153 <label for="url" class="form-label">{{ t('wishlist.create.url') }}</label>
154 <input id="url" v-model="url" type="url" class="form-control" placeholder="https://..." />
155 </div>
156
157 <div class="mb-4">
158 <label for="location" class="form-label">{{ t('wishlist.create.location') }}</label>
159 <input id="location" v-model="location" type="text" class="form-control" :placeholder="t('wishlist.create.locationPlaceholder')" />
160 </div>
161
162 <div class="d-flex gap-2">
163 <button
164 type="button"
165 class="sa-btn sa-btn-danger sa-btn-sm"
166 @click="handleDelete"
167 >
168 <i class="bi bi-trash3"></i> {{ t('common.delete') }}
169 </button>
170 <div class="flex-grow-1"></div>
171 <button
172 type="button"
173 class="sa-btn sa-btn-ghost"
174 @click="router.push({ name: 'WishlistIndex', params: { tripId } })"
175 >
176 {{ t('common.cancel') }}
177 </button>
178 <button
179 type="submit"
180 class="sa-btn sa-btn-primary"
181 :class="{ 'sa-btn-loading': isSaving }"
182 :disabled="isSaving"
183 >
184 {{ isSaving ? t('wishlist.create.saving') : t('common.saveChanges') }}
185 </button>
186 </div>
187 </form>
188 </div>
189 </div>
190 </div>
191 </div>
192 </template>
193