Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow users to submit milestone images #184

Merged
merged 1 commit into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions frontend/src/lib/client/schemas.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ export const Body_reset_reset_password_auth_reset_password_postSchema = {
title: 'Body_reset_reset_password_auth_reset_password_post'
} as const;

export const Body_submit_milestone_image_submitted_milestone_images__milestone_id__postSchema = {
properties: {
file: {
type: 'string',
format: 'binary',
title: 'File'
}
},
type: 'object',
required: ['file'],
title: 'Body_submit_milestone_image_submitted_milestone_images__milestone_id__post'
} as const;

export const Body_upload_child_image_users_children_images__child_id__putSchema = {
properties: {
file: {
Expand Down Expand Up @@ -819,6 +832,26 @@ export const QuestionTextPublicSchema = {
title: 'QuestionTextPublic'
} as const;

export const SubmittedMilestoneImagePublicSchema = {
properties: {
id: {
type: 'integer',
title: 'Id'
},
milestone_id: {
type: 'integer',
title: 'Milestone Id'
},
user_id: {
type: 'integer',
title: 'User Id'
}
},
type: 'object',
required: ['id', 'milestone_id', 'user_id'],
title: 'SubmittedMilestoneImagePublic'
} as const;

export const UserAnswerPublicSchema = {
properties: {
answer: {
Expand Down
47 changes: 46 additions & 1 deletion frontend/src/lib/client/services.gen.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts

import { createClient, createConfig, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from '@hey-api/client-fetch';
import type { GetLanguagesError, GetLanguagesResponse, GetMilestonesError, GetMilestonesResponse, GetMilestoneData, GetMilestoneError, GetMilestoneResponse, GetMilestoneGroupsData, GetMilestoneGroupsError, GetMilestoneGroupsResponse, GetUserQuestionsError, GetUserQuestionsResponse, GetChildQuestionsError, GetChildQuestionsResponse, CreateLanguageData, CreateLanguageError, CreateLanguageResponse, DeleteLanguageData, DeleteLanguageError, DeleteLanguageResponse, UpdateI18NData, UpdateI18NError, UpdateI18NResponse, GetMilestoneGroupsAdminError, GetMilestoneGroupsAdminResponse, CreateMilestoneGroupAdminError, CreateMilestoneGroupAdminResponse, UpdateMilestoneGroupAdminData, UpdateMilestoneGroupAdminError, UpdateMilestoneGroupAdminResponse, DeleteMilestoneGroupAdminData, DeleteMilestoneGroupAdminError, DeleteMilestoneGroupAdminResponse, OrderMilestoneGroupsAdminData, OrderMilestoneGroupsAdminError, OrderMilestoneGroupsAdminResponse, UploadMilestoneGroupImageData, UploadMilestoneGroupImageError, UploadMilestoneGroupImageResponse, CreateMilestoneData, CreateMilestoneError, CreateMilestoneResponse, UpdateMilestoneData, UpdateMilestoneError, UpdateMilestoneResponse, DeleteMilestoneData, DeleteMilestoneError, DeleteMilestoneResponse, OrderMilestonesAdminData, OrderMilestonesAdminError, OrderMilestonesAdminResponse, UploadMilestoneImageData, UploadMilestoneImageError, UploadMilestoneImageResponse, DeleteMilestoneImageData, DeleteMilestoneImageError, DeleteMilestoneImageResponse, GetMilestoneAgeScoresData, GetMilestoneAgeScoresError, GetMilestoneAgeScoresResponse, GetUserQuestionsAdminError, GetUserQuestionsAdminResponse, UpdateUserQuestionData, UpdateUserQuestionError, UpdateUserQuestionResponse, CreateUserQuestionError, CreateUserQuestionResponse, DeleteUserQuestionData, DeleteUserQuestionError, DeleteUserQuestionResponse, OrderUserQuestionsAdminData, OrderUserQuestionsAdminError, OrderUserQuestionsAdminResponse, GetChildQuestionsAdminError, GetChildQuestionsAdminResponse, UpdateChildQuestionData, UpdateChildQuestionError, UpdateChildQuestionResponse, CreateChildQuestionError, CreateChildQuestionResponse, DeleteChildQuestionData, DeleteChildQuestionError, DeleteChildQuestionResponse, OrderChildQuestionsAdminData, OrderChildQuestionsAdminError, OrderChildQuestionsAdminResponse, GetUsersError, GetUsersResponse, UsersCurrentUserError, UsersCurrentUserResponse, UsersPatchCurrentUserData, UsersPatchCurrentUserError, UsersPatchCurrentUserResponse, UsersUserData, UsersUserError, UsersUserResponse, UsersPatchUserData, UsersPatchUserError, UsersPatchUserResponse, UsersDeleteUserData, UsersDeleteUserError, UsersDeleteUserResponse, GetChildrenError, GetChildrenResponse, UpdateChildData, UpdateChildError, UpdateChildResponse, CreateChildData, CreateChildError, CreateChildResponse, GetChildData, GetChildError, GetChildResponse, DeleteChildData, DeleteChildError, DeleteChildResponse, GetChildImageData, GetChildImageError, GetChildImageResponse, UploadChildImageData, UploadChildImageError, UploadChildImageResponse, DeleteChildImageData, DeleteChildImageError, DeleteChildImageResponse, GetCurrentMilestoneAnswerSessionData, GetCurrentMilestoneAnswerSessionError, GetCurrentMilestoneAnswerSessionResponse, UpdateMilestoneAnswerData, UpdateMilestoneAnswerError, UpdateMilestoneAnswerResponse, GetCurrentUserAnswersError, GetCurrentUserAnswersResponse, UpdateCurrentUserAnswersData, UpdateCurrentUserAnswersError, UpdateCurrentUserAnswersResponse, GetCurrentChildAnswersData, GetCurrentChildAnswersError, GetCurrentChildAnswersResponse, UpdateCurrentChildAnswersData, UpdateCurrentChildAnswersError, UpdateCurrentChildAnswersResponse, AuthCookieLoginData, AuthCookieLoginError, AuthCookieLoginResponse, AuthCookieLogoutError, AuthCookieLogoutResponse, RegisterRegisterData, RegisterRegisterError, RegisterRegisterResponse, ResetForgotPasswordData, ResetForgotPasswordError, ResetForgotPasswordResponse, ResetResetPasswordData, ResetResetPasswordError, ResetResetPasswordResponse, VerifyRequestTokenData, VerifyRequestTokenError, VerifyRequestTokenResponse, VerifyVerifyData, VerifyVerifyError, VerifyVerifyResponse, AuthError, AuthResponse } from './types.gen';
import type { GetLanguagesError, GetLanguagesResponse, GetMilestonesError, GetMilestonesResponse, GetMilestoneData, GetMilestoneError, GetMilestoneResponse, GetMilestoneGroupsData, GetMilestoneGroupsError, GetMilestoneGroupsResponse, SubmitMilestoneImageData, SubmitMilestoneImageError, SubmitMilestoneImageResponse, GetUserQuestionsError, GetUserQuestionsResponse, GetChildQuestionsError, GetChildQuestionsResponse, CreateLanguageData, CreateLanguageError, CreateLanguageResponse, DeleteLanguageData, DeleteLanguageError, DeleteLanguageResponse, UpdateI18NData, UpdateI18NError, UpdateI18NResponse, GetMilestoneGroupsAdminError, GetMilestoneGroupsAdminResponse, CreateMilestoneGroupAdminError, CreateMilestoneGroupAdminResponse, UpdateMilestoneGroupAdminData, UpdateMilestoneGroupAdminError, UpdateMilestoneGroupAdminResponse, DeleteMilestoneGroupAdminData, DeleteMilestoneGroupAdminError, DeleteMilestoneGroupAdminResponse, OrderMilestoneGroupsAdminData, OrderMilestoneGroupsAdminError, OrderMilestoneGroupsAdminResponse, UploadMilestoneGroupImageData, UploadMilestoneGroupImageError, UploadMilestoneGroupImageResponse, CreateMilestoneData, CreateMilestoneError, CreateMilestoneResponse, UpdateMilestoneData, UpdateMilestoneError, UpdateMilestoneResponse, DeleteMilestoneData, DeleteMilestoneError, DeleteMilestoneResponse, OrderMilestonesAdminData, OrderMilestonesAdminError, OrderMilestonesAdminResponse, UploadMilestoneImageData, UploadMilestoneImageError, UploadMilestoneImageResponse, DeleteMilestoneImageData, DeleteMilestoneImageError, DeleteMilestoneImageResponse, GetSubmittedMilestoneImagesError, GetSubmittedMilestoneImagesResponse, ApproveSubmittedMilestoneImageData, ApproveSubmittedMilestoneImageError, ApproveSubmittedMilestoneImageResponse, DeleteSubmittedMilestoneImageData, DeleteSubmittedMilestoneImageError, DeleteSubmittedMilestoneImageResponse, GetMilestoneAgeScoresData, GetMilestoneAgeScoresError, GetMilestoneAgeScoresResponse, GetUserQuestionsAdminError, GetUserQuestionsAdminResponse, UpdateUserQuestionData, UpdateUserQuestionError, UpdateUserQuestionResponse, CreateUserQuestionError, CreateUserQuestionResponse, DeleteUserQuestionData, DeleteUserQuestionError, DeleteUserQuestionResponse, OrderUserQuestionsAdminData, OrderUserQuestionsAdminError, OrderUserQuestionsAdminResponse, GetChildQuestionsAdminError, GetChildQuestionsAdminResponse, UpdateChildQuestionData, UpdateChildQuestionError, UpdateChildQuestionResponse, CreateChildQuestionError, CreateChildQuestionResponse, DeleteChildQuestionData, DeleteChildQuestionError, DeleteChildQuestionResponse, OrderChildQuestionsAdminData, OrderChildQuestionsAdminError, OrderChildQuestionsAdminResponse, GetUsersError, GetUsersResponse, UsersCurrentUserError, UsersCurrentUserResponse, UsersPatchCurrentUserData, UsersPatchCurrentUserError, UsersPatchCurrentUserResponse, UsersUserData, UsersUserError, UsersUserResponse, UsersPatchUserData, UsersPatchUserError, UsersPatchUserResponse, UsersDeleteUserData, UsersDeleteUserError, UsersDeleteUserResponse, GetChildrenError, GetChildrenResponse, UpdateChildData, UpdateChildError, UpdateChildResponse, CreateChildData, CreateChildError, CreateChildResponse, GetChildData, GetChildError, GetChildResponse, DeleteChildData, DeleteChildError, DeleteChildResponse, GetChildImageData, GetChildImageError, GetChildImageResponse, UploadChildImageData, UploadChildImageError, UploadChildImageResponse, DeleteChildImageData, DeleteChildImageError, DeleteChildImageResponse, GetCurrentMilestoneAnswerSessionData, GetCurrentMilestoneAnswerSessionError, GetCurrentMilestoneAnswerSessionResponse, UpdateMilestoneAnswerData, UpdateMilestoneAnswerError, UpdateMilestoneAnswerResponse, GetCurrentUserAnswersError, GetCurrentUserAnswersResponse, UpdateCurrentUserAnswersData, UpdateCurrentUserAnswersError, UpdateCurrentUserAnswersResponse, GetCurrentChildAnswersData, GetCurrentChildAnswersError, GetCurrentChildAnswersResponse, UpdateCurrentChildAnswersData, UpdateCurrentChildAnswersError, UpdateCurrentChildAnswersResponse, AuthCookieLoginData, AuthCookieLoginError, AuthCookieLoginResponse, AuthCookieLogoutError, AuthCookieLogoutResponse, RegisterRegisterData, RegisterRegisterError, RegisterRegisterResponse, ResetForgotPasswordData, ResetForgotPasswordError, ResetForgotPasswordResponse, ResetResetPasswordData, ResetResetPasswordError, ResetResetPasswordResponse, VerifyRequestTokenData, VerifyRequestTokenError, VerifyRequestTokenResponse, VerifyVerifyData, VerifyVerifyError, VerifyVerifyResponse, AuthError, AuthResponse } from './types.gen';

export const client = createClient(createConfig());

Expand Down Expand Up @@ -45,6 +45,21 @@ export const getMilestoneGroups = <ThrowOnError extends boolean = false>(options
});
};

/**
* Submit Milestone Image
*/
export const submitMilestoneImage = <ThrowOnError extends boolean = false>(options: Options<SubmitMilestoneImageData, ThrowOnError>) => {
return (options?.client ?? client).post<SubmitMilestoneImageResponse, SubmitMilestoneImageError, ThrowOnError>({
...options,
...formDataBodySerializer,
headers: {
'Content-Type': null,
...options?.headers
},
url: '/submitted-milestone-images/{milestone_id}'
});
};

/**
* Get User Questions
*/
Expand Down Expand Up @@ -225,6 +240,36 @@ export const deleteMilestoneImage = <ThrowOnError extends boolean = false>(optio
});
};

/**
* Get Submitted Milestone Images
*/
export const getSubmittedMilestoneImages = <ThrowOnError extends boolean = false>(options?: Options<unknown, ThrowOnError>) => {
return (options?.client ?? client).get<GetSubmittedMilestoneImagesResponse, GetSubmittedMilestoneImagesError, ThrowOnError>({
...options,
url: '/admin/submitted-milestone-images/'
});
};

/**
* Approve Submitted Milestone Image
*/
export const approveSubmittedMilestoneImage = <ThrowOnError extends boolean = false>(options: Options<ApproveSubmittedMilestoneImageData, ThrowOnError>) => {
return (options?.client ?? client).post<ApproveSubmittedMilestoneImageResponse, ApproveSubmittedMilestoneImageError, ThrowOnError>({
...options,
url: '/admin/submitted-milestone-images/approve/{submitted_milestone_image_id}'
});
};

/**
* Delete Submitted Milestone Image
*/
export const deleteSubmittedMilestoneImage = <ThrowOnError extends boolean = false>(options: Options<DeleteSubmittedMilestoneImageData, ThrowOnError>) => {
return (options?.client ?? client).delete<DeleteSubmittedMilestoneImageResponse, DeleteSubmittedMilestoneImageError, ThrowOnError>({
...options,
url: '/admin/submitted-milestone-images/{submitted_milestone_image_id}'
});
};

/**
* Get Milestone Age Scores
*/
Expand Down
45 changes: 45 additions & 0 deletions frontend/src/lib/client/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ export type Body_reset_reset_password_auth_reset_password_post = {
password: string;
};

export type Body_submit_milestone_image_submitted_milestone_images__milestone_id__post = {
file: (Blob | File);
};

export type Body_upload_child_image_users_children_images__child_id__put = {
file: (Blob | File);
};
Expand Down Expand Up @@ -212,6 +216,12 @@ export type QuestionTextPublic = {
options?: string;
};

export type SubmittedMilestoneImagePublic = {
id: number;
milestone_id: number;
user_id: number;
};

export type UserAnswerPublic = {
answer: string;
additional_answer: (string | null);
Expand Down Expand Up @@ -309,6 +319,17 @@ export type GetMilestoneGroupsResponse = (Array<MilestoneGroupPublic>);

export type GetMilestoneGroupsError = (HTTPValidationError);

export type SubmitMilestoneImageData = {
body: Body_submit_milestone_image_submitted_milestone_images__milestone_id__post;
path: {
milestone_id: number;
};
};

export type SubmitMilestoneImageResponse = (unknown);

export type SubmitMilestoneImageError = (HTTPValidationError);

export type GetUserQuestionsResponse = (Array<UserQuestionPublic>);

export type GetUserQuestionsError = unknown;
Expand Down Expand Up @@ -452,6 +473,30 @@ export type DeleteMilestoneImageResponse = (unknown);

export type DeleteMilestoneImageError = (HTTPValidationError);

export type GetSubmittedMilestoneImagesResponse = (Array<SubmittedMilestoneImagePublic>);

export type GetSubmittedMilestoneImagesError = unknown;

export type ApproveSubmittedMilestoneImageData = {
path: {
submitted_milestone_image_id: number;
};
};

export type ApproveSubmittedMilestoneImageResponse = (unknown);

export type ApproveSubmittedMilestoneImageError = (HTTPValidationError);

export type DeleteSubmittedMilestoneImageData = {
path: {
submitted_milestone_image_id: number;
};
};

export type DeleteSubmittedMilestoneImageResponse = (unknown);

export type DeleteSubmittedMilestoneImageError = (HTTPValidationError);

export type GetMilestoneAgeScoresData = {
path: {
milestone_id: number;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/components/Admin/Languages.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async function deleteLanguageAndUpdateLanguages() {
<TableHead>
<TableHeadCell>Code (ISO 639-1)</TableHeadCell>
<TableHeadCell>Name</TableHeadCell>
<TableHeadCell>Actions</TableHeadCell>
<TableHeadCell>{$_('admin.actions')}</TableHeadCell>
</TableHead>
<TableBody>
{#each $locales as lang_id}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/components/Admin/Questions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ onMount(async () => {
<TableHeadCell>Question</TableHeadCell>
<TableHeadCell>Input type</TableHeadCell>
<TableHeadCell>Options</TableHeadCell>
<TableHeadCell>Actions</TableHeadCell>
<TableHeadCell>{$_('admin.actions')}</TableHeadCell>
</TableHead>
<TableBody>
{#each $questions as question, groupIndex (question.id)}
Expand Down
12 changes: 8 additions & 4 deletions frontend/src/lib/components/Admin/SaveButton.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
<svelte:options runes={true} />
<svelte:options runes={true}/>

<script lang="ts">
import CheckOutline from "flowbite-svelte-icons/CheckOutline.svelte";
import Button from "flowbite-svelte/Button.svelte";
import { _ } from "svelte-i18n";

let { onclick, disabled = false }: { onclick: () => void; disabled?: boolean } =
$props();
let {
onclick,
text = $_("admin.save-changes"),
disabled = false,
}: { onclick: () => void; text?: string; disabled?: boolean } = $props();
</script>

<Button color="green" {onclick} {disabled}
><CheckOutline class="me-2 h-5 w-5" /> {$_('admin.save-changes')}</Button
>
<CheckOutline class="me-2 h-5 w-5"/> {text}</Button
>
116 changes: 116 additions & 0 deletions frontend/src/lib/components/Admin/SubmittedMilestoneImages.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<svelte:options runes={true}/>

<script lang="ts">
import { refreshMilestoneGroups } from "$lib/admin.svelte";
import {
approveSubmittedMilestoneImage,
deleteSubmittedMilestoneImage,
getSubmittedMilestoneImages,
} from "$lib/client/services.gen";
import type { SubmittedMilestoneImagePublic } from "$lib/client/types.gen";
import DeleteButton from "$lib/components/Admin/DeleteButton.svelte";
import DeleteModal from "$lib/components/Admin/DeleteModal.svelte";
import SaveButton from "$lib/components/Admin/SaveButton.svelte";
import { milestoneGroups } from "$lib/stores/adminStore";
import {
Card,
Table,
TableBody,
TableBodyCell,
TableBodyRow,
TableHead,
TableHeadCell,
} from "flowbite-svelte";
import { onMount } from "svelte";
import { _, locale } from "svelte-i18n";

let images = $state([] as Array<SubmittedMilestoneImagePublic>);
let currentImageId = $state(0);
let showDeleteModal: boolean = $state(false);

async function refreshImages() {
const { data, error } = await getSubmittedMilestoneImages();
if (error || !data) {
console.log(error);
} else {
images = data;
}
}

async function deleteCurrentImage() {
const { data, error } = await deleteSubmittedMilestoneImage({
path: {
submitted_milestone_image_id: currentImageId,
},
});
if (error || !data) {
console.log(error);
} else {
await refreshImages();
}
}

async function approveImage(image_id: number) {
const { data, error } = await approveSubmittedMilestoneImage({
path: {
submitted_milestone_image_id: image_id,
},
});
if (error || !data) {
console.log(error);
} else {
await refreshImages();
await refreshMilestoneGroups();
}
}

onMount(async () => {
await refreshImages();
});
</script>

{#if $locale}
<Card size="xl" class="m-5 w-full">
<h3 class="mb-3 text-xl font-medium text-gray-900 dark:text-white">
{$_("admin.users")}
</h3>
<Table>
<TableHead>
<TableHeadCell>{$_('admin.milestone')}</TableHeadCell>
<TableHeadCell>{$_('admin.image')}</TableHeadCell>
<TableHeadCell>{$_('admin.actions')}</TableHeadCell>
</TableHead>
<TableBody>
{#each $milestoneGroups as milestoneGroup (milestoneGroup.id)}
{@const groupTitle = milestoneGroup.text[$locale].title}
{#each milestoneGroup.milestones as milestone (milestone.id)}
{@const milestoneTitle = `${groupTitle} / ${milestone.text[$locale].title}`}
{#each images as image (image.id)}
{#if image.milestone_id === milestone.id}
<TableBodyRow>
<TableBodyCell>
{milestoneTitle}
</TableBodyCell>
<TableBodyCell>
<img src={`${import.meta.env.VITE_MONDEY_API_URL}/static/ms/${image.id}.webp`}
alt={`${image.id}`}/>
</TableBodyCell>
<TableBodyCell>
<SaveButton text={$_("admin.approve")} onclick={() => {approveImage(image.id)}}/>
<DeleteButton onclick={() => {
currentImageId = image.id;
showDeleteModal = true;
}}
/>
</TableBodyCell>
</TableBodyRow>
{/if}
{/each}
{/each}
{/each}
</TableBody>
</Table>
</Card>
{/if}

<DeleteModal bind:open={showDeleteModal} onclick={deleteCurrentImage}></DeleteModal>
2 changes: 1 addition & 1 deletion frontend/src/lib/components/Admin/Users.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ onMount(async () => {
<TableHeadCell>Verified</TableHeadCell>
<TableHeadCell>Researcher</TableHeadCell>
<TableHeadCell>Admin</TableHeadCell>
<TableHeadCell>Actions</TableHeadCell>
<TableHeadCell>{$_('admin.actions')}</TableHeadCell>
</TableHead>
<TableBody>
{#each users as user (user.id)}
Expand Down
Loading