Skip to content

Commit 7291fe4

Browse files
committed
QUIZ-195: ui for flashcard feature
1 parent fa06bcf commit 7291fe4

35 files changed

+4407
-1672
lines changed

components.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22
"$schema": "https://shadcn-vue.com/schema.json",
33
"style": "new-york",
44
"typescript": true,
5-
"tsConfigPath": "./tsconfig.json",
65
"tailwind": {
7-
"config": "tailwind.config.js",
6+
"config": "tailwind.config.ts",
87
"css": "src/assets/index.css",
9-
"baseColor": "slate",
10-
"cssVariables": true
8+
"baseColor": "neutral",
9+
"cssVariables": true,
10+
"prefix": ""
1111
},
12-
"framework": "vite",
1312
"aliases": {
1413
"components": "@/components",
15-
"utils": "@/lib/utils"
16-
}
14+
"composables": "@/composables",
15+
"utils": "@/lib/utils",
16+
"ui": "@/components/ui",
17+
"lib": "@/lib"
18+
},
19+
"iconLibrary": "lucide"
1720
}

package-lock.json

Lines changed: 1848 additions & 633 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"build": "vite build",
1111
"type-check": "vue-tsc --build --force",
1212
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
13-
"format": "prettier --write src/"
13+
"format": "prettier --write src/",
14+
"shadcn-vue": "shadcn-vue"
1415
},
1516
"dependencies": {
1617
"@google/generative-ai": "^0.21.0",
@@ -19,11 +20,11 @@
1920
"@stomp/stompjs": "^7.0.0",
2021
"@vueup/vue-quill": "^1.2.0",
2122
"@vueuse/components": "^10.11.0",
22-
"@vueuse/core": "^11.1.0",
23+
"@vueuse/core": "^11.3.0",
2324
"@vueuse/integrations": "^11.0.3",
2425
"@vueuse/motion": "2.2.6",
2526
"@vueuse/router": "^11.1.0",
26-
"class-variance-authority": "^0.7.0",
27+
"class-variance-authority": "^0.7.1",
2728
"clsx": "^2.1.1",
2829
"driver.js": "^1.3.1",
2930
"lodash-es": "^4.17.21",
@@ -34,10 +35,12 @@
3435
"pinia": "^2.1.7",
3536
"qrcode-vue3": "^1.7.1",
3637
"radix-vue": "^1.9.5",
38+
"reka-ui": "^2.4.1",
3739
"sanitize-html": "^2.13.1",
40+
"shadcn-vue": "^2.2.0",
3841
"socket.io-client": "^4.8.1",
3942
"swrv": "^1.0.4",
40-
"tailwind-merge": "^2.5.2",
43+
"tailwind-merge": "^2.6.0",
4144
"tailwindcss-animate": "^1.0.7",
4245
"uuid": "^10.0.0",
4346
"vee-validate": "^4.13.2",

src/assets/index.css

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,66 +5,86 @@
55
@layer base {
66
:root {
77
--background: 0 0% 100%;
8-
--foreground: 222.2 84% 4.9%;
8+
--foreground: 0 0% 3.9%;
99

10-
--muted: 210 40% 96.1%;
11-
--muted-foreground: 215.4 16.3% 46.9%;
10+
--muted: 0 0% 96.1%;
11+
--muted-foreground: 0 0% 45.1%;
1212

1313
--popover: 0 0% 100%;
14-
--popover-foreground: 222.2 84% 4.9%;
14+
--popover-foreground: 0 0% 3.9%;
1515

1616
--card: 0 0% 100%;
17-
--card-foreground: 222.2 84% 4.9%;
17+
--card-foreground: 0 0% 3.9%;
1818

19-
--border: 214.3 31.8% 91.4%;
20-
--input: 214.3 31.8% 91.4%;
19+
--border: 0 0% 89.8%;
20+
--input: 0 0% 89.8%;
2121

22-
--primary: 222.2 47.4% 11.2%;
23-
--primary-foreground: 210 40% 98%;
22+
--primary: 0 0% 9%;
23+
--primary-foreground: 0 0% 98%;
2424

25-
--secondary: 210 40% 96.1%;
26-
--secondary-foreground: 220.9 39.3% 11%;
25+
--secondary: 0 0% 96.1%;
26+
--secondary-foreground: 0 0% 9%;
2727

28-
--accent: 210 40% 96.1%;
29-
--accent-foreground: 222.2 47.4% 11.2%;
28+
--accent: 0 0% 96.1%;
29+
--accent-foreground: 0 0% 9%;
3030

3131
--destructive: 0 84.2% 60.2%;
32-
--destructive-foreground: 210 40% 98%;
32+
--destructive-foreground: 0 0% 98%;
3333

34-
--ring: 222.2 84% 4.9%;
34+
--ring: 0 0% 3.9%;
3535

3636
--radius: 0.5rem;
37+
38+
--chart-1: 12 76% 61%;
39+
40+
--chart-2: 173 58% 39%;
41+
42+
--chart-3: 197 37% 24%;
43+
44+
--chart-4: 43 74% 66%;
45+
46+
--chart-5: 27 87% 67%;
3747
}
3848

3949
.dark {
40-
--background: 222.2 84% 4.9%;
41-
--foreground: 210 40% 98%;
50+
--background: 0 0% 3.9%;
51+
--foreground: 0 0% 98%;
4252

43-
--muted: 217.2 32.6% 17.5%;
44-
--muted-foreground: 215 20.2% 65.1%;
53+
--muted: 0 0% 14.9%;
54+
--muted-foreground: 0 0% 63.9%;
4555

46-
--popover: 222.2 84% 4.9%;
47-
--popover-foreground: 210 40% 98%;
56+
--popover: 0 0% 3.9%;
57+
--popover-foreground: 0 0% 98%;
4858

49-
--card: 222.2 84% 4.9%;
50-
--card-foreground: 210 40% 98%;
59+
--card: 0 0% 3.9%;
60+
--card-foreground: 0 0% 98%;
5161

52-
--border: 217.2 32.6% 17.5%;
53-
--input: 217.2 32.6% 17.5%;
62+
--border: 0 0% 14.9%;
63+
--input: 0 0% 14.9%;
5464

55-
--primary: 210 40% 98%;
56-
--primary-foreground: 222.2 47.4% 11.2%;
65+
--primary: 0 0% 98%;
66+
--primary-foreground: 0 0% 9%;
5767

58-
--secondary: 217.2 32.6% 17.5%;
59-
--secondary-foreground: 210 40% 98%;
68+
--secondary: 0 0% 14.9%;
69+
--secondary-foreground: 0 0% 98%;
6070

61-
--accent: 217.2 32.6% 17.5%;
62-
--accent-foreground: 210 40% 98%;
71+
--accent: 0 0% 14.9%;
72+
--accent-foreground: 0 0% 98%;
6373

6474
--destructive: 0 62.8% 30.6%;
65-
--destructive-foreground: 210 40% 98%;
75+
--destructive-foreground: 0 0% 98%;
76+
77+
--ring: 0 0% 83.1%;
78+
79+
--chart-1: 220 70% 50%;
80+
81+
--chart-2: 160 60% 45%;
82+
83+
--chart-3: 30 80% 55%;
84+
85+
--chart-4: 280 65% 60%;
6686

67-
--ring: 212.7 26.8% 83.9%;
87+
--chart-5: 340 75% 55%;
6888
}
6989
}
7090

src/components/base/TextareaValidation.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ onBeforeMount(() => {
2828
v-bind="{ ...$props, ...$attrs }"
2929
v-model="value"
3030
:invalid="errorMessage"
31+
class="hover:border-primary border"
3132
/>
3233
<ErrorMessage
3334
:error="errorMessage"
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<script lang="ts" setup>
2+
import Textarea from '@/components/ui/textarea/Textarea.vue'
3+
import { Card } from '@/components/ui/card'
4+
5+
const props = defineProps<{
6+
number: number
7+
term: string
8+
description: string
9+
image?: string
10+
lengthData: number
11+
}>()
12+
13+
const emit = defineEmits<{
14+
(e: 'update:term', value: string): void
15+
(e: 'update:description', value: string): void
16+
(e: 'update:image', value: string): void
17+
(e: 'remove'): void
18+
}>()
19+
20+
const refImage = ref<HTMLInputElement>()
21+
22+
const showChooseImage = () => {
23+
refImage.value?.click()
24+
}
25+
26+
const onChangeImage = (e: Event) => {
27+
const target = e.target as HTMLInputElement
28+
const file = target?.files?.[0]
29+
if (file) {
30+
const url = URL.createObjectURL(file)
31+
emit('update:image', url)
32+
}
33+
}
34+
35+
const removeImage = () => {
36+
emit('update:image', '')
37+
}
38+
</script>
39+
40+
<template>
41+
<Card class="border border-[#edeff4] bg-white rounded-xl">
42+
<div class="flex items-center justify-between p-5">
43+
<p class="font-bold text-xl text-slate-500">{{ number }}</p>
44+
<div
45+
v-if="props.lengthData > 1"
46+
class="group flex items-center rounded-full hover:border-red-400 justify-center w-8 h-8 border border-[#edeff4] cursor-pointer"
47+
@click="emit('remove')"
48+
>
49+
<span
50+
class="i-material-symbols-light-delete-outline cursor-pointer text-xl text-slate-500 group-hover:text-red-500"
51+
></span>
52+
</div>
53+
</div>
54+
<div class="line w-full border border-[#f6f7fb]"></div>
55+
<div class="flex items-start justify-between gap-5 px-5 py-8">
56+
<!-- term -->
57+
<div class="grid w-full gap-1.5">
58+
<Textarea
59+
:model-value="term"
60+
placeholder="Enter your term"
61+
@update:model-value="(val) => emit('update:term', String(val))"
62+
/>
63+
<Label class="text-[12px]">TERM</Label>
64+
</div>
65+
66+
<!-- description -->
67+
<div class="grid w-full gap-1.5">
68+
<Textarea
69+
:model-value="description"
70+
placeholder="Enter your description"
71+
@update:model-value="(val) => emit('update:description', String(val))"
72+
/>
73+
<Label class="text-[12px]">DESCRIPTION</Label>
74+
</div>
75+
76+
<!-- upload -->
77+
<div class="flex flex-col items-center">
78+
<div
79+
v-if="props.image"
80+
class="relative w-[100px] h-[100px] border rounded-lg"
81+
>
82+
<div class="w-[100px] h-[100px] overflow-hidden rounded-lg">
83+
<img
84+
:src="props.image"
85+
class="object-cover w-full h-full"
86+
/>
87+
</div>
88+
<button
89+
class="absolute -top-2 -right-2 bg-white p-1 border rounded-full w-4 h-4 flex items-center justify-center text-xs shadow"
90+
@click="removeImage"
91+
>
92+
93+
</button>
94+
</div>
95+
<div
96+
v-else
97+
class="cursor-pointer group items-center justify-center flex p-3 rounded-lg border-dashed border-[#d9dde8] border-2 w-[100px] h-[100px] hover:border-primary"
98+
@click="showChooseImage"
99+
>
100+
<span
101+
class="i-material-symbols-light-add-a-photo-sharp cursor-pointer text-xl text-slate-500 group-hover:text-primary"
102+
></span>
103+
</div>
104+
105+
<input
106+
ref="refImage"
107+
type="file"
108+
accept="image/jpeg, image/png, image/jpg"
109+
class="hidden"
110+
@change="onChangeImage"
111+
/>
112+
</div>
113+
</div>
114+
</Card>
115+
</template>

0 commit comments

Comments
 (0)