Skip to content
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
5 changes: 5 additions & 0 deletions .changes/optimize-problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"algohub": patch:feat
---

Optimize problem pages, including create and edit pages.
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 44 additions & 14 deletions src/components/ProblemEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const accountStore = useAccountStore();

const props = defineProps<{ id?: string }>();
const loading = ref(props.id ? true : false);
const path = ref<{ label?: string, link?: string }[]>(props.id ? [] : [{ label: 'New Problem' }])
onMounted(async () => {
if (props.id) {
const res = await api.fetchProblem(props.id, accountStore.auth);
Expand All @@ -30,6 +31,9 @@ onMounted(async () => {
hint.value = data.hint || '';
testCases.splice(0, testCases.length);
data.test_cases?.forEach(tc => testCases.push({ input: tc.input, output: tc.output }));

path.value = [{ label: res.data?.owner.id, link: `/account/${res.data?.owner.id}` }, { label: title.value, link: `/problem/${props.id}` }, { label: 'Edit' }]

loading.value = false;
}
})
Expand Down Expand Up @@ -83,14 +87,20 @@ const validate = (form: ProblemForm<string, number>): boolean => {

const inProgress = ref(false);
const onCreateProblem = async () => {
if (totalSizePercent.value !== 100) {
uploadingTestCases.value = true;
await uploadTestCases(() => {
uploadingTestCases.value = false;
})
}
const problem: ProblemForm<string, number> = {
title: title.value,
description: description.value,
input: input.value || undefined,
output: output.value || undefined,
samples: samples.map(sample => ({ input: sample.input, output: sample.output })),
hint: hint.value || undefined,
time_limit: timeLimit.value,
time_limit: timeLimit.value * 1024 * 1024,
memory_limit: memoryLimit.value,
test_cases: testCases.map(tc => ({ input: tc.input, output: tc.output })),
owner: {
Expand All @@ -106,17 +116,27 @@ const onCreateProblem = async () => {

if (inProgress.value) return;
inProgress.value = true;
const res = await api.createProblem({
id: accountStore.account.id!,
token: accountStore.account.token!,
...problem,
});
let res;
if (props.id) {
res = await api.updateProblem(props.id, {
id: accountStore.account.id!,
token: accountStore.account.token!,
...problem,
});
} else {
res = await api.createProblem({
id: accountStore.account.id!,
token: accountStore.account.token!,
...problem,
});
}
if (!res.success) {
inProgress.value = false;
return toast.add({ severity: 'error', summary: 'Error', detail: res.message, life: 3000 });
}
inProgress.value = false;
router.push(`/problem/${res.data!.id}`);
const id = res.data?.id ?? props.id;
router.push(`/problem/${id}`);
}

const totalSize = ref(0);
Expand Down Expand Up @@ -155,9 +175,13 @@ const onSelectedFiles = (event: FileUploadSelectEvent) => {
event.files.forEach((file: File) => {
totalSize.value += parseInt(formatSize(file.size));
});

totalUploadedSize.value = 0;
};

const uploadingTestCases = ref(false);
const uploadTestCases = async (callback: () => void) => {
uploadingTestCases.value = true;
normalizedFiles.value.forEach(async (fileTuple) => {
if (!fileTuple.input) {
return toast.add({ severity: 'error', summary: 'Error', detail: 'Input file not found for ' + fileTuple.output?.name, life: 3000 });
Expand Down Expand Up @@ -192,6 +216,7 @@ const uploadTestCases = async (callback: () => void) => {
output: outputRes.data!.id,
})
normalizedFiles.value.splice(normalizedFiles.value.indexOf(fileTuple), 1);
uploadingTestCases.value = false;
});
callback();
}
Expand Down Expand Up @@ -253,8 +278,12 @@ const onRemoveTestCases = async (testCase: TestCase) => {
</script>

<template>
<div class="max-w-full md:max-w-[768px] mx-auto">
<Panel v-if="!loading" class="mt-10">
<UniversalToolBar :path></UniversalToolBar>
<div class="flex flex-col w-full h-full max-w-full md:max-w-[768px] mx-auto">
<div v-if="loading" class="w-full h-full flex items-center justify-center">
<ProgressSpinner></ProgressSpinner>
</div>
<Panel v-else class="mt-10">
<div class="flex flex-col gap-8">
<div v-if="!props.id" class="mt-10 text-center">
<span class="text-gray-500 mb-4">Share your algorithm problem with the community</span>
Expand Down Expand Up @@ -356,8 +385,8 @@ const onRemoveTestCases = async (testCase: TestCase) => {
<div v-if="testCase.input" class="flex flex-col gap-4 items-center">
<span
class="font-semibold text-ellipsis max-w-60 whitespace-nowrap overflow-hidden">{{
testCase.input.name }}</span>
<div>{{ formatSize(testCase.input.size || 0) }}</div>
testCase.input?.name }}</span>
<div>{{ formatSize(testCase.input?.size || 0) }}</div>
<Badge value="Pending" severity="warn" />
<Button icon="pi pi-times"
@click="onRemoveTemplatingFile('input', { removeFileCallback, plainFiles: files, normalizedIndex })"
Expand All @@ -368,8 +397,8 @@ const onRemoveTestCases = async (testCase: TestCase) => {
<div v-if="testCase.output" class="flex flex-col gap-4 items-center">
<span
class="font-semibold text-ellipsis max-w-60 whitespace-nowrap overflow-hidden">{{
testCase.output.name }}</span>
<div>{{ formatSize(testCase.output.size || 0) }}</div>
testCase.output?.name }}</span>
<div>{{ formatSize(testCase.output?.size || 0) }}</div>
<Badge value="Pending" severity="warn" />
<Button icon="pi pi-times"
@click="onRemoveTemplatingFile('output', { removeFileCallback, plainFiles: files, normalizedIndex })"
Expand Down Expand Up @@ -399,7 +428,8 @@ const onRemoveTestCases = async (testCase: TestCase) => {
</template>
</Column>
</DataTable>
<Button @click="onCreateProblem" type="submit" label="Save Changes"></Button>
<Button @click="onCreateProblem" type="submit" label="Save Changes" :loading="inProgress"
:disabled="uploadingTestCases"></Button>
</div>
</div>
</Panel>
Expand Down
9 changes: 9 additions & 0 deletions src/scripts/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ export const createProblem = async (form: CreateProblem) => {
}
};

export const updateProblem = async (id: string, form: CreateProblem) => {
try {
const response = await axios.post(`/problem/update/${id}`, form);
return response.data as Response<undefined>;
} catch (error) {
return handleAxiosError(AxiosError.from(error));
}
};

export const fetchProblem = async (id: string, form?: Credentials) => {
try {
const response = await axios.post(`/problem/get/${id}`, form);
Expand Down
2 changes: 1 addition & 1 deletion src/views/problem/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const formatProblem = (problem: UserProblem) => {
}
hint && (formattedText += `## Hint\n\n${hint}\n\n`);

return formattedText.repeat(10);
return formattedText;
}

const code = ref('');
Expand Down
7 changes: 1 addition & 6 deletions src/views/problem/create.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
<script setup lang="ts">
const path = [{ label: 'New problem' }];
</script>

<template>
<div class="flex-1 flex flex-col">
<UniversalToolBar :path></UniversalToolBar>
<div class="flex-1 flex flex-col h-full">
<ProblemEditor></ProblemEditor>
<UniversalFooter></UniversalFooter>
</div>
Expand Down
5 changes: 1 addition & 4 deletions src/views/problem/edit/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ import { useRoute } from 'vue-router';

const route = useRoute();
const id = route.params.id as string;

const path = [{ label: 'New problem' }];
</script>

<template>
<div class="flex-1 flex flex-col">
<UniversalToolBar :path></UniversalToolBar>
<div class="flex-1 flex flex-col h-full">
<ProblemEditor :id></ProblemEditor>
<UniversalFooter></UniversalFooter>
</div>
Expand Down
Loading