Delete todo app, add blog website

This commit is contained in:
Riyyi
2025-03-31 00:49:47 +02:00
parent 57ea6a88d1
commit ebad4817be
17 changed files with 1231 additions and 2906 deletions
+8 -6
View File
@@ -1,16 +1,18 @@
<template>
<div>
<h2 v-if="$slots.namedslot">
<strong v-if="$slots.namedslot">
<slot name="namedslot" mdc-unwrap="p" />
</h2>
</strong>
<h2 v-else>Broken Slot</h2>
<slot mdc-unwrap="p" />
<p>
<slot mdc-unwrap="p" />
</p>
</div>
</template>
<script setup lang="ts">
defineProps({
namedslot: String,
});
defineProps({
namedslot: String,
});
</script>
+9
View File
@@ -1,2 +1,11 @@
<template>
<footer class="row mb-2">
<div class="col-12">
© {{ year }} Rick van Vonderen
</div>
</footer>
</template>
<script setup lang="ts">
const year = new Date().getFullYear();
</script>
+28 -59
View File
@@ -1,61 +1,30 @@
<template>
<div class="card">
<Menubar :model="items">
<template #item="{ item, props, hasSubmenu }">
<router-link v-if="item.route" v-slot="{ href, navigate }" :to="item.route" custom>
<a v-ripple :href="href" v-bind="props.action" @click="navigate">
<span :class="item.icon" />
<span>{{ item.label }}</span>
</a>
</router-link>
<a v-else v-ripple :href="item.url" :target="item.target" v-bind="props.action">
<span :class="item.icon" />
<span>{{ item.label }}</span>
<span v-if="hasSubmenu" class="pi pi-fw pi-angle-down" />
</a>
</template>
</Menubar>
</div>
<BNavbar v-b-color-mode="'dark'" toggleable="lg" variant="dark" fixed="top">
<BNavbarBrand to="/">
<IFaHome /> Home
</BNavbarBrand>
<BNavbarToggle target="nav-collapse" />
<BCollapse id="nav-collapse" is-nav>
<!-- Left aligned nav items -->
<BNavbarNav>
<BNavItem to="/articles">Articles</BNavItem>
<BNavItem to="/todos">Todos</BNavItem>
</BNavbarNav>
<!-- Right aligned nav items -->
<BNavbarNav class="ms-auto mb-2 mb-lg-0">
<BNavItem href="https://git.riyyi.com/riyyi" target="_blank">
<IFaCoffee /> Gitea
</BNavItem>
<BNavItem href="https://github.com/riyyi" target="_blank">
<IFaGithub /> Github
</BNavItem>
<BNavItem href="https://gitlab.com/riyyi" target="_blank">
<IFaGitlab /> Gitlab
</BNavItem>
<BNavItem href="https://linkedin.com/in/rickvanvonderen" target="_blank">Linked
<IFaLinkedinSquare />
</BNavItem>
</BNavbarNav>
</BCollapse>
</BNavbar>
</template>
<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
const router = useRouter();
const currentDate = new Date().toLocaleString("nl-NL");
const items = ref([
{
label: "Home",
icon: "pi pi-home",
route: "/"
},
{
label: "Todos",
icon: "pi pi-check",
command: () => {
router.push("/todos");
}
},
{
label: "Articles",
icon: "pi pi-link",
route: "/articles"
},
{
label: "Service",
icon: "pi pi-link",
route: "#"
},
{
label: "Contact",
icon: "pi pi-link",
route: "#"
},
{
label: "Date: " + currentDate,
},
]);
</script>
-18
View File
@@ -1,18 +0,0 @@
<template>
adasda
<Column field="title">
<template #header>
<span @click="toggleSorting('title')" class="p-datatable-column-title">Title&nbsp;
<span :class="['pi', sorting.title === 'asc' ? 'pi-arrow-circle-down' : 'pi-arrow-circle-up']"></span>
</span>
</template>
</Column>
</template>
<script setup lang="ts">
const sorting = defineModel<Record<string, string>>({ required: true });
function toggleSorting(key: string) {
sorting.value[key] = sorting.value[key] === 'asc' ? 'desc' : 'asc';
};
</script>
-59
View File
@@ -1,59 +0,0 @@
<template>
<Form v-slot="$form" ref="formRef" :initialValues :resolver @submit="onFormSubmit"
class="flex flex-col gap-4 w-full sm:w-56">
<!-- {{ $form }} -->
<div class="flex flex-col gap-1">
<InputText @input="initial = false;" v-model="formData.title" name="title" type="text" placeholder="Title"
autocomplete="off" fluid />
<Message v-if="$form.title?.invalid" severity="error" size="small" variant="simple">
{{ $form.title.error?.message }}
</Message>
</div>
<Button :disabled="initial || !$form.valid" class="fr" type="submit" severity="secondary" label="Submit" />
</Form>
</template>
<script setup lang="ts">
import { useTodoStore } from "@/stores/todoStore";
import { todoSchema } from "@/schemas/todo";
import { zodResolver } from '@primevue/forms/resolvers/zod';
import { v4 as uuidv4 } from "uuid";
import { type FormSubmitEvent } from "@primevue/forms/form";
const store = useTodoStore();
const toast = useToast();
const resolver = zodResolver(todoSchema);
const initial = ref(true); // makes submit button disabled
const initialValues = ref({
title: ""
});
const formRef = ref<HTMLElement | null>(null);
const formData = ref({ ...initialValues.value }); // copy data from initialValues
async function onFormSubmit(e: FormSubmitEvent) {
if (e.valid) {
toast.add({ severity: "success", summary: "Todo added", life: 3000 });
store.todos.push({
id: uuidv4(),
title: e.values?.title || e.states?.title.value
});
// Reset the form
initial.value = true;
formData.value = { ...initialValues.value }; // copy data from initialValues
}
else {
toast.add({ severity: "error", summary: "Invalid request", life: 3000 });
}
};
</script>
<style scoped>
.fr {
float: right;
}
</style>
-94
View File
@@ -1,94 +0,0 @@
<template>
<div class="card">
<ConfirmDialog></ConfirmDialog>
<DataTable :value="computedTodos" :lazy="true" tableStyle="min-width: 50rem" removableSort
:sortMode="'multiple'" v-model:multiSortMeta="multiSortMeta">
<Column field="number" header="#" />
<Column field="id" header="ID" sortable />
<Column field="title" header="Title" sortable />
<Column header="Modifier">
<template #body="slotProps">
<a @click="removeTodo(slotProps.data.id)" v-tooltip.top="'Delete'" class="pointer">
<span class="pi pi-trash" style="color: var(--p-red-600);"></span>
</a>
</template>
</Column>
</DataTable>
</div>
</template>
<script setup lang="ts">
import { useConfirm } from "primevue/useconfirm";
import { useToast } from "primevue/usetoast";
import { useTodoStore } from "@/stores/todoStore";
import type { DataTableSortMeta } from "primevue/datatable";
import type { Todo } from "@/schemas/todo";
const confirm = useConfirm();
const store = useTodoStore();
const toast = useToast();
// Reactive bindings for sorting
const multiSortMeta = ref<DataTableSortMeta[]>([]);
function sortTodos(): Todo[] {
let todos: Todo[] = JSON.parse(JSON.stringify(store.todos)); // deep copy
// Loop in reverse order to apply the sorting correctly
for (let i = multiSortMeta.value.length - 1; i >= 0; i--) {
const sort: DataTableSortMeta = multiSortMeta.value[i];
const value = (item: Todo): string => {
if (typeof sort.field === "string") {
return item[sort.field as keyof Todo] || "";
}
else if (typeof sort.field === "function") {
return sort.field(item) || "";
}
return "";
};
todos.sort((a, b) => sort.order! * value(a).localeCompare(value(b)));
}
return todos;
}
const computedTodos = computed(() => {
return sortTodos().map((todo, index) => ({
number: index + 1,
...todo
}));
});
const removeTodo = (id: String) => {
confirm.require({
message: "Do you want to delete this todo?",
header: "Confirmation",
icon: "pi pi-info-circle",
rejectLabel: "Cancel",
rejectProps: {
label: "Cancel",
severity: "secondary",
outlined: true
},
acceptProps: {
label: "Delete",
severity: "danger"
},
accept: () => {
toast.add({ severity: "info", summary: "Confirmed", detail: "Todo deleted", life: 3000 });
store.todos = store.todos.filter(todo => todo.id !== id);
},
reject: () => {}
});
};
</script>
<style scoped>
.pointer {
cursor: pointer;
}
</style>