diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7825a44..4d5c57d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,7 +11,9 @@ "@fortawesome/fontawesome-free": "^6.4.2", "@sveltejs/adapter-auto": "^2.1.0", "@sveltejs/kit": "^1.24.1", - "axios": "^1.5.0" + "axios": "^1.5.0", + "svelte-forms-lib": "^2.0.1", + "yup": "^1.2.0" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^2.4.2", @@ -1756,6 +1758,11 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/property-expr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", + "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -2071,6 +2078,14 @@ "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0" } }, + "node_modules/svelte-forms-lib": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/svelte-forms-lib/-/svelte-forms-lib-2.0.1.tgz", + "integrity": "sha512-kwbJ3ynsepsrrJyAMrvSc0Lj/myc9vfI2DL8OKxgArZimrNYsRh1gENYhvrcKEI3BiZrv8q3VFfmGo/GMyk7Zg==", + "dependencies": { + "dequal": "^2.0.2" + } + }, "node_modules/svelte-hmr": { "version": "0.15.3", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz", @@ -2226,6 +2241,11 @@ "node": ">=0.8" } }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" + }, "node_modules/tiny-glob": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", @@ -2247,6 +2267,11 @@ "node": ">=8.0" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -2267,6 +2292,17 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -2408,6 +2444,17 @@ "engines": { "node": ">= 14" } + }, + "node_modules/yup": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.2.0.tgz", + "integrity": "sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ==", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } } } } diff --git a/frontend/package.json b/frontend/package.json index 01ae778..916c447 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,8 @@ "@fortawesome/fontawesome-free": "^6.4.2", "@sveltejs/adapter-auto": "^2.1.0", "@sveltejs/kit": "^1.24.1", - "axios": "^1.5.0" + "axios": "^1.5.0", + "svelte-forms-lib": "^2.0.1", + "yup": "^1.2.0" } } diff --git a/frontend/src/lib/ButtonMain.svelte b/frontend/src/lib/ButtonMain.svelte new file mode 100644 index 0000000..9e72b45 --- /dev/null +++ b/frontend/src/lib/ButtonMain.svelte @@ -0,0 +1,11 @@ + + + diff --git a/frontend/src/lib/ButtonTrash.svelte b/frontend/src/lib/ButtonTrash.svelte new file mode 100644 index 0000000..c7d482c --- /dev/null +++ b/frontend/src/lib/ButtonTrash.svelte @@ -0,0 +1,11 @@ + + + diff --git a/frontend/src/lib/WordForm.svelte b/frontend/src/lib/WordForm.svelte index 45e4116..b281f65 100644 --- a/frontend/src/lib/WordForm.svelte +++ b/frontend/src/lib/WordForm.svelte @@ -1,14 +1,102 @@ -
- -
-
{word}
-
{definition}
+ + + {#if !isEditLevel} + + {:else} + + {/if} + + + {#if !isEditDefinition} +
+ {definition} +
+ {:else} + + {/if} + + + + + diff --git a/frontend/src/lib/arrays.ts b/frontend/src/lib/arrays.ts new file mode 100644 index 0000000..5e0326b --- /dev/null +++ b/frontend/src/lib/arrays.ts @@ -0,0 +1,7 @@ +export function deleteFromArray(arr: any[], obj: any): any[] { + const index = arr.indexOf(obj, 0); + if (index > -1) { + arr.splice(index, 1); + } + return arr; +} \ No newline at end of file diff --git a/frontend/src/routes/ma_geriou/+page.svelte b/frontend/src/routes/ma_geriou/+page.svelte index bd050d9..d8e53c4 100644 --- a/frontend/src/routes/ma_geriou/+page.svelte +++ b/frontend/src/routes/ma_geriou/+page.svelte @@ -2,80 +2,197 @@ import { page } from "$app/stores"; import axios from "$lib/api.ts"; import WordForm from "$lib/WordForm.svelte"; - import { type Word } from "../../lib/types"; + import ButtonMain from "$lib/ButtonMain.svelte"; + import type { Level, Word } from "../../lib/types"; + import { deleteFromArray } from "$lib/arrays"; import { snakeToCamelCase } from "$lib/case"; + import { onMount, tick } from "svelte"; + import type { AxiosResponse } from "axios"; + import { createForm } from "svelte-forms-lib"; + import * as yup from "yup"; let words: Word[]; - const wordResponse = axios.get("words/").then((data) => { - words = snakeToCamelCase(data.data); + $: words; + let levels: Level[]; + let promise; + let promiseLevels; + + // Form + const { form, errors, state, handleChange, handleSubmit } = createForm({ + initialValues: { + level: "", + word: "", + definition: "", + }, + validationSchema: yup.object().shape({ + level: yup.number().required(), + word: yup.string().required(), + definition: yup.string().required(), + }), + onSubmit: (values) => { + submitNewWord(values); + }, }); + + function fetchLevels() { + try { + promiseLevels = axios.get("levels/").then((data) => { + levels = snakeToCamelCase(data.data); + }); + } catch (e) { + console.log(e); + } + } + + function fetchData() { + try { + promise = axios.get("words/").then((data) => { + words = snakeToCamelCase(data.data); + }); + } catch (e) { + console.log(e); + } + } + let isFormNewShown = false; function toggleFormNew() { isFormNewShown = !isFormNewShown; } - function newWord(e) { - const formData = new FormData(e.target); + function submitNewWord(values) { + const formData = { + level_id: values.level, + word: values.word, + definition: values.definition, + }; - const data = {}; - for (let field of formData) { - const [key, value] = field; - data[key] = value; + try { + axios + .post("words/", formData) + .then((data) => { + words = [snakeToCamelCase(data.data), ...words]; + }) + .then(() => { + $form.definition = ""; + $form.word = ""; + }); + } catch (e) { + console.log(e); } - console.log(data); } + + function deleteWord(word: Word) { + if (!(word && word.id)) return; + try { + axios.delete("/words/" + word.id).then(() => { + words = deleteFromArray(words, word); + }); + } catch (e) { + console.log(e); + } + } + + onMount(() => { + fetchData(); + fetchLevels(); + });
- {#await wordResponse} + {#await promise}

{:then} -
-
Live
-
Ger
-
Termenadur
+
+ + + + + + + + - {#if !isFormNewShown} -
- -
- {:else} - -
- -
-
- -
-
- -
- - {/if} - - {#each words as word} - - {/each} -
+
+ {#if !isFormNewShown} + + + + {:else} + + + + + + + {/if} + {#if words} + {#each words as word} + + {/each} + {/if} + +
LiveGerTermenadur +
+ + + +
+ + + + + + + +
+ + {:catch error} + {error} {/await}
diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index a2553f7..601dd5b 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -1,4 +1,4 @@ -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' +import { vitePreprocess } from '@sveltejs/kit/vite' import adapter from "@sveltejs/adapter-auto" diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index c4e1c5f..e323630 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/svelte/tsconfig.json", + "extends": "./.svelte-kit/tsconfig.json", "compilerOptions": { "target": "ESNext", "useDefineForClassFields": true, @@ -15,6 +15,15 @@ "checkJs": true, "isolatedModules": true }, - "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], - "references": [{ "path": "./tsconfig.node.json" }] -} + "include": [ + "src/**/*.d.ts", + "src/**/*.ts", + "src/**/*.js", + "src/**/*.svelte" + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} \ No newline at end of file diff --git a/grids/serializers.py b/grids/serializers.py index e039bf0..b622ebb 100644 --- a/grids/serializers.py +++ b/grids/serializers.py @@ -10,10 +10,22 @@ class LevelSerializer(serializers.ModelSerializer): class WordSerializer(serializers.ModelSerializer): level = LevelSerializer(many=False, read_only=True) + level_id = serializers.PrimaryKeyRelatedField( + write_only=True, source="level", queryset=Level.objects.all() + ) class Meta: model = Word - fields = ["id", "word", "definition", "level"] + fields = ["id", "word", "definition", "level", "level_id"] + + # def create(self, validated_data): + # print(validated_data) + # level_data = validated_data.pop("level") + # level = Level.objects.get(pk=level_data) + + # word = Word.objects.create(level=level, **validated_data) + + # return word class GridSerializer(serializers.ModelSerializer):