Skip to content

Commit 97c0ff6

Browse files
committed
whole buncha stuff
1 parent 4d4053f commit 97c0ff6

6 files changed

Lines changed: 157 additions & 64 deletions

File tree

ui/components/global/submitButton.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
>
2121
{{ props.submitText }}
2222
</button>
23-
<button v-if="props.submitLoading" type="button" :class="[colorClassesDisabled, bttnSize]" class="w-full inline-flex items-center justify-center justify-center border border-transparent font-medium rounded-md-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 cursor-default" disabled>
23+
<button v-if="props.submitLoading" type="button" :class="[colorClassesDisabled, bttnSize]" class="w-full inline-flex items-center justify-center justify-center border border-transparent font-medium rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 cursor-default" disabled>
2424
<svg :class="spinnerSize" class="animate-spin -ml-1 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
2525
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
2626
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
@@ -36,6 +36,8 @@ import { PropType } from 'vue'
3636
3737
type ButtonSizes = "xs" | "sm" | "md" | "lg" | "xl"
3838
39+
const emits = defineEmits(["submit"])
40+
3941
const props = defineProps({
4042
submitText: {
4143
type: String,

ui/models/RiddlesAnswerResponse.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
export type RiddleAnswer = {
3+
success: boolean
4+
}

ui/pages/[id].vue

Lines changed: 108 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,80 @@
2020
</div>
2121
<div v-else-if="!riddle?.isSolved && timer.hours < 24">
2222
<div class="mt-6 flex items-end space-x-2">
23-
<div class="sm:w-1/2">
24-
<label for="answer" class="block text-sm font-medium text-gray-700">Answer</label>
25-
<div class="mt-1">
26-
<input type="text" name="answer" id="answer" class="block w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500 focus:ring-teal-500 transition-hover-300" placeholder="Super smart answer">
23+
<div class="w-full flex space-x-2">
24+
<div class="w-full">
25+
<label for="answer" class="block text-sm font-medium text-gray-700">Answer</label>
26+
<div class="mt-1">
27+
<input v-model="answer" type="text" name="answer" id="answer" class="block w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500 focus:ring-teal-500 transition-hover-300" placeholder="Super smart answer">
28+
</div>
29+
</div>
30+
<div class="w-full">
31+
<label v-if="apiKey === ''" for="api-key" class="block text-sm font-medium text-gray-700">API Key</label>
32+
<label v-else for="api-key" class="block text-sm font-medium text-gray-700">API Key (we cached it)</label>
33+
<div class="relative mt-1">
34+
<input v-model="apiKey" :type="apiKeyInputShow" name="api-key" id="api-key" class="block w-full rounded-md border-gray-300 shadow-sm focus:border-teal-500 focus:ring-teal-500 transition-hover-300" placeholder="Super smart answer">
35+
<button v-if="apiKeyInputShow === 'password'" @click="apiKeyInputShow = 'text'" class="absolute inset-y-0 right-0 flex items-center pr-3 focus:outline-none">
36+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 text-gray-500 hover:text-gray-900 transition-hover-300">
37+
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
38+
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
39+
</svg>
40+
</button>
41+
<button v-if="apiKeyInputShow === 'text'" @click="apiKeyInputShow = 'password'" class="absolute inset-y-0 right-0 flex items-center pr-3 focus:outline-none">
42+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 text-gray-500 hover:text-gray-900 transition-hover-300">
43+
<path stroke-linecap="round" stroke-linejoin="round" d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88" />
44+
</svg>
45+
</button>
46+
</div>
47+
</div>
48+
</div>
49+
<SubmitButton @submit="submitAnswer()" submit-text="Submit" color="teal" size="lg" :is-valid-state="!answerIsError && answer !== '' && apiKey !== ''" :submit-loading="answerIsLoading" />
50+
</div>
51+
52+
<div v-if="answerIsCorrect" class="mt-4 rounded-md bg-green-50 p-4">
53+
<div class="flex">
54+
<div class="flex-shrink-0">
55+
<svg class="h-5 w-5 text-green-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
56+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
57+
</svg>
58+
</div>
59+
<div class="ml-3">
60+
<h3 class="text-sm font-medium text-green-800">Correct</h3>
61+
<div class="mt-2 text-sm text-green-700">
62+
<p>Congrats, you answered today's riddle correctly!</p>
63+
</div>
2764
</div>
2865
</div>
29-
<SubmitButton submit-text="Submit" color="teal" size="lg" :is-valid-state="true" :submit-loading="false" />
3066
</div>
67+
68+
<div v-if="answerIsCorrect === false" class="mt-4 rounded-md bg-red-50 p-4">
69+
<div class="flex">
70+
<div class="flex-shrink-0">
71+
<!-- Heroicon name: mini/x-circle -->
72+
<svg class="h-5 w-5 text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
73+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
74+
</svg>
75+
</div>
76+
<div class="ml-3">
77+
<h3 class="text-sm font-medium text-red-800">Incorrect answer, dumbass</h3>
78+
</div>
79+
</div>
80+
</div>
81+
82+
<div v-if="answerIsError" class="mt-4 rounded-md bg-red-50 p-4">
83+
<div class="flex">
84+
<div class="flex-shrink-0">
85+
<!-- Heroicon name: mini/x-circle -->
86+
<svg class="h-5 w-5 text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
87+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
88+
</svg>
89+
</div>
90+
<div class="ml-3">
91+
<h3 class="text-sm font-medium text-red-800">An error occured when submitting the request. Did you provide the correct API key?</h3>
92+
</div>
93+
</div>
94+
</div>
95+
96+
3197
</div>
3298
<div v-else class="mt-6 flex items-center space-x-2">
3399
<span>
@@ -43,12 +109,13 @@
43109

44110
<script setup lang="ts">
45111
import { Riddle } from '../models/RiddlesResponse'
112+
import { RiddleAnswer } from '../models/RiddlesAnswerResponse'
46113
import { differenceInSeconds } from 'date-fns'
47114
48115
const route = useRoute()
49116
const riddleId = route.params.id
50117
51-
const { data: riddle, error:riddleError } = await useFetch<Riddle>('/api/riddles/' + riddleId)
118+
const { data: riddle, error:riddleError , refresh: refetchRiddle } = await useFetch<Riddle>('/api/riddles/' + riddleId)
52119
53120
if(!riddle.value?.isAvailable) {
54121
navigateTo("/")
@@ -69,5 +136,40 @@ const timer = useTimer(secondsUntilExpiry.value!)
69136
70137
71138
// SUBMIT ANSWER
139+
const answer = ref<string>('')
140+
const apiKey = useCookie('apiKey')
141+
const apiKeyInputShow = ref("password")
142+
143+
const answerIsCorrect = ref<boolean | null>(null)
144+
const answerIsLoading = ref(false)
145+
const answerIsError = ref(false)
146+
147+
const submitAnswer = async () => {
148+
answerIsLoading.value = true
149+
const { data: answerRes, error:answerError} = await useFetch<RiddleAnswer>("/api/riddles/answers/" + riddleId, {
150+
method: "PUT",
151+
body: {
152+
answer: answer.value
153+
},
154+
headers: useRequestHeaders(['cookie']) as Record<string,string>
155+
})
156+
157+
if(answerError.value) {
158+
answerIsError.value = true
159+
answerIsLoading.value = false
160+
return
161+
}
162+
163+
if(answerRes.value?.success) {
164+
answerIsCorrect.value = true
165+
answerIsLoading.value = false
166+
setTimeout(() => {
167+
refetchRiddle()
168+
}, 3000);
169+
} else {
170+
answerIsCorrect.value = false
171+
answerIsLoading.value = false
172+
}
173+
}
72174
73175
</script>

ui/server/api/riddles/[id].get.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,17 @@ const config = useRuntimeConfig()
66
export default defineEventHandler(async (event): Promise<Riddle> => {
77
const id = event.context.params.id
88

9-
try {
10-
const riddle = await $fetch<Riddle>(config.apiBase + "/riddles/" + id)
9+
const riddle = await $fetch<Riddle>(config.apiBase + "/riddles/" + id)
1110

12-
if (riddle === null) {
13-
throw createError({ statusCode: 500, message: "Failed to get riddle"})
14-
}
15-
16-
if(new Date(riddle.effective) < new Date()) {
17-
riddle.isAvailable = true
18-
} else {
19-
riddle.isAvailable = false
20-
}
21-
22-
return riddle
23-
} catch(er) {
24-
throw createError({ statusCode: 500, message: "Failed to get riddles"})
11+
if (riddle === null) {
12+
throw createError({ statusCode: 500, message: "Failed to get riddle"})
13+
}
14+
15+
if(new Date(riddle.effective) < new Date()) {
16+
riddle.isAvailable = true
17+
} else {
18+
riddle.isAvailable = false
2519
}
20+
21+
return riddle
2622
})
Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,28 @@
1-
import { Riddle } from '../../../models/RiddlesResponse'
2-
import { $fetch, FetchError } from 'ohmyfetch'
1+
import { RiddleAnswer } from '../../../../models/RiddlesAnswerResponse'
2+
import { $fetch } from 'ohmyfetch'
3+
import { zh, z } from 'h3-zod';
34

45
const config = useRuntimeConfig()
56

6-
export default defineEventHandler(async (event): Promise<Riddle> => {
7+
export default defineEventHandler(async (event): Promise<RiddleAnswer> => {
8+
const cookies = parseCookies(event)
9+
const apiKey = cookies.apiKey
10+
711
const id = event.context.params.id
8-
912

10-
try {
11-
const riddle = await $fetch<Riddle>(config.apiBase + "/riddles/" + id, {
12-
method: "PUT",
13-
body: {
14-
answer: "foo"
15-
}
16-
})
13+
const body = await zh.useValidatedBody(event, z.object({
14+
answer: z.string()
15+
}))
1716

18-
if (riddle === null) {
19-
throw createError({ statusCode: 500, message: "Failed to get riddle"})
20-
}
21-
22-
if(new Date(riddle.effective) < new Date()) {
23-
riddle.isAvailable = true
24-
} else {
25-
riddle.isAvailable = false
17+
const answerRes = await $fetch<RiddleAnswer>(config.apiBase + "/riddles/answers/" + id, {
18+
method: "PUT",
19+
body: {
20+
answer: body.answer
21+
},
22+
headers: {
23+
"x-api-key": apiKey
2624
}
25+
})
2726

28-
return riddle
29-
} catch(er) {
30-
throw createError({ statusCode: 500, message: "Failed to get riddles"})
31-
}
27+
return answerRes
3228
})

ui/server/api/riddles/index.get.ts

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,19 @@ import { $fetch, FetchError } from 'ohmyfetch'
44
const config = useRuntimeConfig()
55

66
export default defineEventHandler(async (): Promise<Riddle[]> => {
7-
try {
8-
const riddles = await $fetch<Riddle[]>(config.apiBase + "/riddles")
7+
const riddles = await $fetch<Riddle[]>(config.apiBase + "/riddles")
98

10-
if (riddles === null) {
11-
throw createError({ statusCode: 500, message: "Failed to get riddles"})
12-
}
13-
14-
riddles.forEach(r => {
15-
console.log(new Date(r.effective))
16-
console.log(new Date())
17-
18-
if(new Date(r.effective) < new Date()) {
19-
r.isAvailable = true
20-
} else {
21-
r.isAvailable = false
22-
}
23-
})
24-
25-
return riddles
26-
} catch(er) {
9+
if (riddles === null) {
2710
throw createError({ statusCode: 500, message: "Failed to get riddles"})
2811
}
12+
13+
riddles.forEach(r => {
14+
if(new Date(r.effective) < new Date()) {
15+
r.isAvailable = true
16+
} else {
17+
r.isAvailable = false
18+
}
19+
})
20+
21+
return riddles
2922
})

0 commit comments

Comments
 (0)