diff --git a/packages/api/src/models/user.ts b/packages/api/src/models/user.ts index 94b0863..beffe3d 100644 --- a/packages/api/src/models/user.ts +++ b/packages/api/src/models/user.ts @@ -34,6 +34,7 @@ const userSchema = new Schema({ verified: { type: Boolean, required: true, default: false }, favoriteBatteries: [{ type: Schema.Types.ObjectId, ref: "Battery" }], recentBatteries: [{ type: Schema.Types.ObjectId, ref: "Battery" }], + welcomeWizardStep: { type: Number, required: true, default: 0 }, }); userSchema.methods.verifyPassword = function (password: string) { diff --git a/packages/api/src/services/auth.service.ts b/packages/api/src/services/auth.service.ts index 83ed081..de8fdbe 100644 --- a/packages/api/src/services/auth.service.ts +++ b/packages/api/src/services/auth.service.ts @@ -130,8 +130,7 @@ export const getUsers = async (): APIResponse => { export const updateUser = async (req: any): APIResponse => { const user = req.user; if (!user) throw new HttpError(401, "Unauthorized"); - - const { firstName, lastName, email, password } = + const { firstName, lastName, email, password, welcomeWizardStep } = req.body as Partial & { password?: string; }; @@ -141,6 +140,8 @@ export const updateUser = async (req: any): APIResponse => { if (typeof email === "string") user.email = email; if (typeof password === "string" && password.length > 0) user.password = password; + if (typeof welcomeWizardStep === "number") + user.welcomeWizardStep = welcomeWizardStep; await user.save(); return [200, user]; diff --git a/packages/shared/types/models/user.ts b/packages/shared/types/models/user.ts index af59012..7d6fb96 100644 --- a/packages/shared/types/models/user.ts +++ b/packages/shared/types/models/user.ts @@ -18,6 +18,7 @@ export interface CreateUser { favoriteBatteries?: Types.ObjectId[]; recentStudyIds?: string[] | null; recentBatteries?: Types.ObjectId[]; + welcomeWizardStep: number; } export interface IUser extends Required { diff --git a/packages/ui/public/hearing.svg b/packages/ui/public/hearing.svg new file mode 100644 index 0000000..475e2c8 --- /dev/null +++ b/packages/ui/public/hearing.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/public/training.svg b/packages/ui/public/training.svg new file mode 100644 index 0000000..189d1a9 --- /dev/null +++ b/packages/ui/public/training.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/ui/src/components/AuthForm.vue b/packages/ui/src/components/AuthForm.vue index a864796..c125e98 100644 --- a/packages/ui/src/components/AuthForm.vue +++ b/packages/ui/src/components/AuthForm.vue @@ -68,7 +68,7 @@ function submit() { v-if="!hasPasswordConfirm" class="px-8" @click="router.push('/signup')" - >SignupSign Up -import { ref } from "vue"; +import { ref, reactive } from "vue"; import { useAuthStore } from "../../stores/auth"; import { useRouter } from "vue-router"; import MyStudiesItem from "./components/MyStudiesItem.vue"; import StudyDetailsSidebar from "./components/StudyDetailsSideBar.vue"; import { useQuery, useMutation, useQueryClient } from "@tanstack/vue-query"; +import authAPI from "@/api/auth"; import studiesAPI from "@/api/studies"; import AppButton from "@/components/ui/AppButton.vue"; import RecentStudies from "./components/RecentStudies.vue"; +import { ElDialog, ElForm, ElFormItem, ElInput, ElTag } from "element-plus"; +import { Plus } from "@element-plus/icons-vue"; const router = useRouter(); const authStore = useAuthStore(); @@ -33,6 +36,18 @@ const { mutate, isLoading } = useMutation({ const selectedStudyId = ref(null); const showSidebar = ref(false); +const createStudyPopup = ref(false); +const { data: currentUser } = useQuery({ + queryKey: ["user"], + queryFn: authAPI.getCurrentUser, +}); + +const tagsIcon = { + Cognitive: "/brain.svg", + Hearing: "/hearing.svg", + Training: "/training.svg", + Vision: "/vision.svg", +}; const openSidebar = (studyId: string) => { selectedStudyId.value = studyId; @@ -43,8 +58,40 @@ const handleStudyDeleted = async () => { await refetch(); await queryClient.invalidateQueries({ queryKey: ["studies", "recent"] }); }; - +const firstStudyForm = reactive({ + title: "", + description: "", + tags: [] as string[], + serverCode: "", +}); + +const createFirstStudy = async () => { + createStudyPopup.value = false; + const createdStudyId = await studiesAPI.createStudy(); + const study = await studiesAPI.getStudy(createdStudyId); + await studiesAPI.saveStudy(createdStudyId, { + ...study, + name: firstStudyForm.title, + description: firstStudyForm.description, + variants: study.variants.map((variant, index) => + index === 0 + ? { + ...variant, + serverCode: firstStudyForm.serverCode, + tags: firstStudyForm.tags, + } + : variant + ), + }); + router.push({ name: "study", params: { id: createdStudyId } }); +}; + +const updateWizardSteps = async (step: number) => { + await authAPI.updateCurrentUser({ welcomeWizardStep: step }); + await queryClient.invalidateQueries(["user"]); +}; + + + diff --git a/packages/ui/src/pages/StudyBuilderPage/StudyBuilderPage.vue b/packages/ui/src/pages/StudyBuilderPage/StudyBuilderPage.vue index c6e6dee..48a43a4 100644 --- a/packages/ui/src/pages/StudyBuilderPage/StudyBuilderPage.vue +++ b/packages/ui/src/pages/StudyBuilderPage/StudyBuilderPage.vue @@ -1,7 +1,7 @@ + + diff --git a/packages/ui/src/pages/StudyBuilderPage/components/StudyPanel.vue b/packages/ui/src/pages/StudyBuilderPage/components/StudyPanel.vue index 9602ed7..10e8fb3 100644 --- a/packages/ui/src/pages/StudyBuilderPage/components/StudyPanel.vue +++ b/packages/ui/src/pages/StudyBuilderPage/components/StudyPanel.vue @@ -7,10 +7,22 @@ import StudyServerCode from "./StudyServerCode.vue"; import { ref, watch } from "vue"; import { ArrowRight, ArrowLeft, Plus, Delete } from "@element-plus/icons-vue"; import { useRoute } from "vue-router"; +import authAPI from "@/api/auth"; +import { useQueryClient, useQuery } from "@tanstack/vue-query"; const studyBuilderStore = useStudyBuilderStore(); const route = useRoute(); const currentVariantIndex = ref(0); +const queryClient = useQueryClient(); +const { data: currentUser } = useQuery({ + queryKey: ["user"], + queryFn: authAPI.getCurrentUser, +}); + +const updateWizardSteps = async (step: number) => { + await authAPI.updateCurrentUser({ welcomeWizardStep: step }); + await queryClient.invalidateQueries(["user"]); +}; const switchVariantByIndex = (index: number) => { const variant = studyBuilderStore.variants[index]; @@ -191,14 +203,83 @@ watch( - - - + + + +
+ + diff --git a/packages/ui/src/pages/StudyConditions/components/components/TagLabel.vue b/packages/ui/src/pages/StudyConditions/components/components/TagLabel.vue index 48f14af..c2c68e5 100644 --- a/packages/ui/src/pages/StudyConditions/components/components/TagLabel.vue +++ b/packages/ui/src/pages/StudyConditions/components/components/TagLabel.vue @@ -9,6 +9,8 @@ const props = defineProps<{ const tagIconMap: Record = { cognitive: "/brain.svg", vision: "/vision.svg", + hearing: "/hearing.svg", + training: "/training.svg", }; const icon = computed(() => { diff --git a/packages/ui/src/stores/auth.ts b/packages/ui/src/stores/auth.ts index 3cf24a3..aa3974b 100644 --- a/packages/ui/src/stores/auth.ts +++ b/packages/ui/src/stores/auth.ts @@ -10,6 +10,7 @@ export interface IUser { role: Role; studies: string[]; favoriteBatteries: IBattery[]; + welcomeWizardStep: number; } export const useAuthStore = defineStore("auth", () => {