Skip to content

Commit

Permalink
create study upon clicking + new study (#77)
Browse files Browse the repository at this point in the history
Co-authored-by: Ross Newman <[email protected]>
  • Loading branch information
gmanalang and ross3102 authored Jan 26, 2024
1 parent 872cf60 commit 6e8e504
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 114 deletions.
4 changes: 2 additions & 2 deletions packages/api/src/models/study.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const sessionSchema = new Schema<ISession>({
});

const studySchema = new Schema<IStudy>({
name: { type: String, required: true },
description: { type: String, required: true },
name: { type: String, default: "" },
description: { type: String, default: "" },
batteries: [{ type: Schema.Types.ObjectId, ref: "CustomizedBattery" }],
sessions: [sessionSchema],
});
Expand Down
19 changes: 11 additions & 8 deletions packages/api/src/routes/studies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,25 @@ router.get("/:id", isAuthenticated, (req, res, next) => {
.catch(next);
});

router.post("/", (req, res, next) => {
Study.create(req.body)
.then((study) => res.status(201).json(study))
.catch(next);
router.post("/new", isAuthenticated, async (req, res, next) => {
try {
const user = req.user as HydratedDocument<IUser>;
const study = await Study.create({});

await user.updateOne({ $push: { studies: study._id } });
res.status(201).json(study);
} catch (e) {
next(e);
}
});

router.put("/:id", isAuthenticated, async (req, res, next) => {
try {
const study = await Study.findOneAndUpdate(
{ _id: req.params["id"] },
req.body,
{ upsert: true, new: true }
{ new: true }
);
const user = req.user as HydratedDocument<IUser>;
if (!user.studies.includes(study._id))
await user.updateOne({ $push: { studies: study._id } });
res.json(study);
} catch (e) {
next();
Expand Down
33 changes: 19 additions & 14 deletions packages/ui/src/api/studies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,6 @@ axiosInstance.interceptors.response.use(
}
);

async function getStudies() {
const response = await axiosInstance.get("/studies/");
return response.data;
}

async function deleteStudy(id: string) {
await axiosInstance.delete(`/studies/${id}`);
}

async function saveStudy(id: string, studyData: GetStudyResponse) {
await axiosInstance.put(`/studies/${id}`, studyData);
}

export interface ITaskInstance {
_id: string;
task: string;
Expand All @@ -51,15 +38,33 @@ export interface ICustomizedBattery {
}

export interface GetStudyResponse {
_id: string;
name: string;
description: string;
batteries: ICustomizedBattery[];
sessions: ISession[];
}
async function getStudies() {
const response = await axiosInstance.get<GetStudyResponse[]>("/studies/");
return response.data;
}

async function getStudy(id: string) {
const result = await axiosInstance.get<GetStudyResponse>(`studies/${id}`);
return result.data;
}

export default { getStudies, deleteStudy, getStudy, saveStudy };
async function createStudy() {
const response = await axiosInstance.post<GetStudyResponse>("/studies/new");
return response.data;
}

async function deleteStudy(id: string) {
await axiosInstance.delete(`/studies/${id}`);
}

async function saveStudy(id: string, studyData: GetStudyResponse) {
await axiosInstance.put(`/studies/${id}`, studyData);
}

export default { getStudies, deleteStudy, getStudy, saveStudy, createStudy };
21 changes: 12 additions & 9 deletions packages/ui/src/pages/MyStudiesPage/MyStudiesPage.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<script setup lang="ts">
import { useAuthStore } from "../../stores/auth";
import { RouterLink, useRouter } from "vue-router";
import { useRouter } from "vue-router";
import MyStudiesItem from "./components/MyStudiesItem.vue";
import { useQuery } from "@tanstack/vue-query";
import studiesAPI from "@/api/studies";
import { useMutation } from "@tanstack/vue-query";
import AppButton from "@/components/ui/AppButton.vue";
const router = useRouter();
const authStore = useAuthStore();
Expand All @@ -17,20 +19,21 @@ const { data, refetch } = useQuery({
queryFn: studiesAPI.getStudies,
});
const { mutate, isLoading } = useMutation({
mutationFn: () => studiesAPI.createStudy(),
onSuccess: (createdStudy) => {
router.push({ name: "study", params: { id: createdStudy._id } });
},
});
const studies = data;
</script>

<template>
<div class="mt-14 mx-auto w-3/4 min-w-[600px]">
<div v-loading="isLoading" class="mt-14 mx-auto w-3/4 min-w-[600px]">
<div class="flex items-center">
<h1 class="text-3xl font-bold">My Studies</h1>
<div class="flex-1"></div>
<RouterLink
:to="{ name: 'study', params: { id: 'new' } }"
class="flex text-sm bg-neutral-300 border border-black rounded-lg py-1 w-30 p-4 h-8 justify-center"
>
+ New Study
</RouterLink>
<AppButton @click="mutate">+ New Study</AppButton>
</div>

<div class="flex flex-col">
Expand Down
16 changes: 5 additions & 11 deletions packages/ui/src/pages/MyStudiesPage/components/MyStudiesItem.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import studiesAPI from "@/api/studies";
import { useMutation } from "@tanstack/vue-query";
import AppButton from "@/components/ui/AppButton.vue";
const emit = defineEmits(["deleted"]);
const props = defineProps<{ name: string; description: string; id: string }>();
Expand All @@ -21,17 +22,10 @@ const { mutate } = useMutation({
{{ description }}
</h2>
<div class="flex-1"></div>
<RouterLink
:to="{ name: 'study', params: { id } }"
class="flex flex-none text-sm bg-neutral-300 justify-center border border-black rounded-lg py-1 w-20"
>
Edit

<RouterLink :to="{ name: 'study', params: { id } }">
<AppButton>Edit</AppButton>
</RouterLink>
<button
class="flex flex-none text-sm bg-neutral-300 justify-center border border-black rounded-lg py-1 w-20"
@click="mutate()"
>
Delete
</button>
<AppButton @click="mutate">Delete</AppButton>
</div>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ const draggableProps = {
<div
class="flex-1 flex gap-2 items-end justify-end min-w-[200px] flex-wrap"
>
<StudyServerCode
v-if="!studyBuilderStore.isNewStudy"
class="shrink grow-0 min-w-0"
/>
<StudyServerCode class="shrink grow-0 min-w-0" />
<AppButton class="flex-none" @click="studyBuilderStore.saveStudyStore">
Save Changes
</AppButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const studyBuilderStore = useStudyBuilderStore();
const copied = ref(false);
function copyCode() {
if (!studyBuilderStore.studyId) return;
navigator.clipboard.writeText(studyBuilderStore.studyId).then(() => {
copied.value = true;
setTimeout(() => {
Expand Down
110 changes: 45 additions & 65 deletions packages/ui/src/stores/studyBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ export const useStudyBuilderStore = defineStore("studyBuilder", () => {
const router = useRouter();
const queryClient = useQueryClient();
const { mutate } = useMutation({
mutationFn({
studyId,
...studyData
}: { studyId: string } & GetStudyResponse) {
return studiesAPI.saveStudy(studyId, studyData);
mutationFn(studyData: GetStudyResponse) {
return studiesAPI.saveStudy(studyData._id, studyData);
},
onMutate() {
isStudySaving.value = true;
Expand All @@ -51,17 +48,12 @@ export const useStudyBuilderStore = defineStore("studyBuilder", () => {
});

function routeStudyId() {
if (route.name === "study") {
const idParam = route.params.id;
if (idParam && idParam !== "new") {
return typeof idParam === "string" ? idParam : idParam[0];
}
}
return new mongoose.Types.ObjectId().toString();
if (route.name !== "study") return "";
const idParam = route.params.id;
return typeof idParam === "string" ? idParam : idParam[0];
}

const isNewStudy = ref(true);
const studyId = ref<string>();
const studyId = ref<string>("");
const isStudyLoading = ref(false);
const isStudySaving = ref(false);
const name = ref<string>();
Expand All @@ -73,65 +65,54 @@ export const useStudyBuilderStore = defineStore("studyBuilder", () => {

function initialize() {
if (route.name !== "study") return;
isNewStudy.value = route.params.id == "new";

studyId.value = routeStudyId();
isStudyLoading.value = !isNewStudy.value;
isStudyLoading.value = true;
name.value = undefined;
description.value = undefined;
taskData.value = {};
taskBank.value = [];
sessionData.value = {};
sessions.value = [];

if (isNewStudy.value) {
const EMPTY_SESSION = {
_id: new mongoose.Types.ObjectId().toString(),
name: "",
tasks: [],
};

sessionData.value = { [EMPTY_SESSION._id]: EMPTY_SESSION };
sessions.value = [EMPTY_SESSION._id];
} else {
queryClient
.fetchQuery({
queryKey: ["studies", studyId],
queryFn: () => {
return studiesAPI.getStudy(studyId.value!);
},
})
.then((studyData) => {
name.value = studyData.name;
description.value = studyData.description;
taskData.value = {};
studyData.batteries.forEach((b) => {
taskData.value[b._id] = b;
queryClient
.fetchQuery({
queryKey: ["studies", studyId],
queryFn: () => {
return studiesAPI.getStudy(studyId.value!);
},
})
.then((studyData) => {
name.value = studyData.name;
description.value = studyData.description;
taskData.value = {};
studyData.batteries.forEach((b) => {
taskData.value[b._id] = b;
});
taskBank.value = studyData.batteries.map((b) => b._id);
sessionData.value = {};
studyData.sessions.forEach((s) => {
sessionData.value[s._id] = s;
});
sessions.value = studyData.sessions.map((s) => s._id);
isStudyLoading.value = false;
})
.catch((err: AxiosError<Error>) => {
router.push("/");
if (err.response?.status == 404) {
ElNotification({
title: "Error",
message: "Study not found",
type: "error",
});
taskBank.value = studyData.batteries.map((b) => b._id);
sessionData.value = {};
studyData.sessions.forEach((s) => {
sessionData.value[s._id] = s;
} else {
ElNotification({
title: "Error",
message: "Error loading study",
type: "error",
});
sessions.value = studyData.sessions.map((s) => s._id);
isStudyLoading.value = false;
})
.catch((err: AxiosError<Error>) => {
router.push("/");
if (err.response?.status == 404) {
ElNotification({
title: "Error",
message: "Study not found",
type: "error",
});
} else {
ElNotification({
title: "Error",
message: "Error loading study",
type: "error",
});
}
});
}
}
});
}

const createCustomTaskMutation = useMutation({
Expand Down Expand Up @@ -206,7 +187,7 @@ export const useStudyBuilderStore = defineStore("studyBuilder", () => {
function saveStudyStore() {
if (isStudyLoading.value || isStudySaving.value) return;
mutate({
studyId: studyId.value!,
_id: studyId.value!,
name: name.value ?? "",
description: description.value ?? "",
batteries: taskBank.value.map((id) => taskData.value[id]), // TODO: fix this
Expand All @@ -218,7 +199,6 @@ export const useStudyBuilderStore = defineStore("studyBuilder", () => {

return {
studyId,
isNewStudy,
isStudyLoading,
isStudySaving,
name,
Expand Down

0 comments on commit 6e8e504

Please sign in to comment.