profileShare

rasmusjy / splitapp-frontend-vue

Read-only snapshot

No repository description.

main default branch 96 files Expires Sep 13, 2026, 9:06 AM
CreateView.vue 5,408 bytes
1 <script setup lang="ts">
2 import { ref } from 'vue'
3 import { useRouter, useRoute } from 'vue-router'
4 import { useI18n } from 'vue-i18n'
5 import PollService from '@/services/PollService'
6
7 const router = useRouter()
8 const route = useRoute()
9 const { t } = useI18n()
10
11 const tripId = route.params.tripId as string
12
13 const question = ref('')
14 const allowMultipleVotes = ref(false)
15 const isAnonymous = ref(false)
16 const options = ref<string[]>(['', ''])
17 const errors = ref<string[]>([])
18 const isSaving = ref(false)
19
20 function addOption() {
21 options.value.push('')
22 }
23
24 function removeOption(index: number) {
25 if (options.value.length > 2) {
26 options.value.splice(index, 1)
27 }
28 }
29
30 async function handleSubmit() {
31 errors.value = []
32
33 const filledOptions = options.value.filter((o) => o.trim() !== '')
34 if (filledOptions.length < 2) {
35 errors.value = [t('polls.create.atLeastTwo')]
36 return
37 }
38
39 isSaving.value = true
40
41 const result = await PollService.create({
42 tripId,
43 question: question.value,
44 allowMultipleVotes: allowMultipleVotes.value,
45 isAnonymous: isAnonymous.value,
46 options: filledOptions,
47 })
48
49 if (result.errors) {
50 errors.value = result.errors
51 } else {
52 router.push({ name: 'PollsIndex', params: { tripId } })
53 }
54
55 isSaving.value = false
56 }
57 </script>
58
59 <template>
60 <div style="max-width: 600px; margin: 0 auto">
61 <!-- Header -->
62 <div class="sa-gradient-header sa-gradient-header-blue">
63 <h2 class="mb-0">{{ t('polls.create.title') }}</h2>
64 </div>
65
66 <!-- Errors -->
67 <div v-if="errors.length" class="sa-card-static sa-card-accent sa-card-accent-danger mb-4">
68 <div class="sa-card-body">
69 <div v-for="error in errors" :key="error">
70 <i class="bi bi-exclamation-triangle me-2"></i>{{ error }}
71 </div>
72 </div>
73 </div>
74
75 <!-- Form -->
76 <div class="sa-card-static">
77 <div class="sa-card-body">
78 <form @submit.prevent="handleSubmit">
79 <!-- Question -->
80 <div class="mb-4">
81 <label for="question" class="form-label">{{ t('polls.create.question') }}</label>
82 <input
83 id="question"
84 v-model="question"
85 type="text"
86 class="form-control"
87 :placeholder="t('polls.create.questionPlaceholder')"
88 required
89 />
90 </div>
91
92 <!-- Toggle: Allow Multiple Votes -->
93 <div class="d-flex align-items-center justify-content-between mb-3 p-3" style="background: var(--sa-gray-50); border-radius: var(--sa-radius-sm)">
94 <div>
95 <div class="fw-bold" style="font-size: 0.938rem">{{ t('polls.create.allowMultiple') }}</div>
96 <div style="font-size: 0.8rem; color: var(--sa-gray-500)">{{ t('polls.create.allowMultipleHelp') }}</div>
97 </div>
98 <input
99 id="allowMultiple"
100 v-model="allowMultipleVotes"
101 type="checkbox"
102 class="sa-toggle"
103 />
104 </div>
105
106 <!-- Toggle: Anonymous Voting -->
107 <div class="d-flex align-items-center justify-content-between mb-4 p-3" style="background: var(--sa-gray-50); border-radius: var(--sa-radius-sm)">
108 <div>
109 <div class="fw-bold" style="font-size: 0.938rem">{{ t('polls.create.anonymous') }}</div>
110 <div style="font-size: 0.8rem; color: var(--sa-gray-500)">{{ t('polls.create.anonymousHelp') }}</div>
111 </div>
112 <input
113 id="isAnonymous"
114 v-model="isAnonymous"
115 type="checkbox"
116 class="sa-toggle"
117 />
118 </div>
119
120 <!-- Options -->
121 <div class="mb-4">
122 <label class="form-label">{{ t('polls.create.options') }}</label>
123 <div v-for="(_, index) in options" :key="index" class="d-flex gap-2 mb-2">
124 <input
125 v-model="options[index]"
126 type="text"
127 class="form-control"
128 :placeholder="t('polls.create.optionPlaceholder', { n: index + 1 })"
129 />
130 <button
131 v-if="options.length > 2"
132 type="button"
133 class="sa-btn sa-btn-icon sa-btn-sm sa-btn-ghost"
134 style="flex-shrink: 0"
135 @click="removeOption(index)"
136 >
137 <i class="bi bi-x-lg"></i>
138 </button>
139 </div>
140 <button
141 type="button"
142 class="sa-btn sa-btn-ghost sa-btn-sm"
143 @click="addOption"
144 >
145 <i class="bi bi-plus-lg"></i> {{ t('polls.create.addOption') }}
146 </button>
147 </div>
148
149 <!-- Actions -->
150 <div class="d-flex gap-2">
151 <button
152 type="button"
153 class="sa-btn sa-btn-ghost"
154 @click="router.push({ name: 'PollsIndex', params: { tripId } })"
155 >
156 {{ t('common.cancel') }}
157 </button>
158 <button
159 type="submit"
160 class="sa-btn sa-btn-primary"
161 :class="{ 'sa-btn-loading': isSaving }"
162 :disabled="isSaving"
163 >
164 {{ isSaving ? t('polls.create.creating') : t('polls.create.createPoll') }}
165 </button>
166 </div>
167 </form>
168 </div>
169 </div>
170 </div>
171 </template>
172