Initial commit

This commit is contained in:
Wroclaw 2023-05-11 06:03:22 +02:00
commit 1e63e008af
48 changed files with 12715 additions and 0 deletions

74
pages/client/[id].vue Normal file
View file

@ -0,0 +1,74 @@
<script setup lang="ts">
import { useRoute, useFetch, createError } from "nuxt/app";
import { Ref } from "vue";
import PagedList from "~/components/pagedList.vue";
import { client as clientType } from "~/utils/types/database";
const route = useRoute();
const id = route.params.id;
const clientRequest = await useFetch(`/api/clients/${id}`);
if (clientRequest.error.value) throw createError(clientRequest.error.value?.data ?? "");
const client = clientRequest.data as Ref<clientType>;
console.log(client);
</script>
<template>
<VRow>
<VCol cols="12">
<div
class="text-h4"
:class="client.name === null ? ['font-italic'] : []"
>
{{ client.name ?? "[none]" }}
</div>
</VCol>
</VRow>
<VRow>
<VCol md="4" cols="12">
<VCard>
<VList>
<VListItem
v-if="client.address"
prepend-icon="mdi-map-marker"
>
<VListItemTitle class="text-wrap">
{{ client.address }}
</VListItemTitle>
</VListItem>
<VListItem
v-if="client.email"
prepend-icon="mdi-email"
>
<VListItemTitle class="text-wrap">
{{ client.email }}
</VListItemTitle>
</VListItem>
<VListItem
v-if="client.phone"
prepend-icon="mdi-phone"
>
<VListItemTitle class="text-wrap">
{{ client.phone }}
</VListItemTitle>
</VListItem>
</VList>
</VCard>
</VCol>
<VCol cols="12" md="8">
<PagedList
:records="[{a: 'owo'}, {a: 'uwu'}, {a: 'qwq'}]"
record-key="a"
>
<template #text="i">
{{ i }}
</template>
<template #title="i">
{{ i }}
</template>
</PagedList>
</VCol>
</VRow>
</template>

124
pages/clients.backup.vue Normal file
View file

@ -0,0 +1,124 @@
<script setup lang="ts">
/* global $fetch */
import { useFetch, createError } from "nuxt/app";
import { ref, Ref } from "vue";
import { definePageMeta } from "~/.nuxt/imports";
import { client as clientType } from "~/utils/types/database";
definePageMeta({ middleware: ["auth"] });
const clientsRequest = await useFetch("/api/clients");
if (clientsRequest.error.value) throw createError(clientsRequest.error.value?.data ?? "");
const clients = clientsRequest.data as Ref<NonNullable<typeof clientsRequest.data.value>>;
const countRequest = await useFetch("/api/clients/count");
if (countRequest.error.value) throw createError(countRequest.error.value?.data ?? "");
const count = countRequest.data as Ref<NonNullable<typeof countRequest.data.value>>;
function rowClicked(client: string, edit = false) {
console.log(client);
}
async function rowDelete(client: string) {
try {
await $fetch<clientType>(`/api/clients/${client}`, {
method: "DELETE",
});
clients.value = clients.value.filter(e => e.id !== client);
count.value.count--;
} catch (e) {
// FIXME: show the error
console.log(e);
}
}
const loadingMore = ref<boolean>(false);
async function loadBefore() {
loadingMore.value = true;
clients.value.push(...await $fetch("/api/clients", {
query: {
before: clients.value[clients.value.length - 1].id,
},
}));
loadingMore.value = false;
}
</script>
<template>
<VOverlay
model-value
origin="top center"
:scrim="false"
height="fit-content"
persistent
no-click-animation
>
<VAlert class="alert">
owowowowowowowowo
</VAlert>
</VOverlay>
<VRow>
<VCol>
<VBreadcrumbs :items="['Clients']" />
<VSpacer />
<div class="text-h4">
There are {{ count?.count }} clients in the database.
</div>
</VCol>
</VRow>
<VRow>
<VCol cols="12">
<VCard>
<VTable>
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th />
</tr>
</thead>
<tbody>
<tr v-for="client in clients" :key="client.id">
<td @click="() => rowClicked(client.id)">
{{ client.name }}
</td>
<td @click="() => rowClicked(client.id)">
{{ client.address }}
</td>
<td class="buttons">
<div>
<VBtn icon="mdi-pencil" variant="text" @click="() => rowClicked(client.id, true)" />
<VBtn icon="mdi-delete" color="red" variant="text" @click="() => rowDelete(client.id)" />
</div>
</td>
</tr>
</tbody>
</VTable>
</VCard>
<VCol>
<VBtn
v-if="clients.length < count.count"
color="primary"
:loading="loadingMore"
@click="loadBefore"
>
Load more
</VBtn>
</VCol>
</VCol>
</VRow>
</template>
<style scoped>
.buttons {
width: 0;
padding-right: 4px !important;
}
.buttons > div {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
</style>

105
pages/clients.vue Normal file
View file

@ -0,0 +1,105 @@
<script setup lang="ts">
/* global $fetch */
import { useFetch, createError } from "nuxt/app";
import { ref, Ref } from "vue";
import { definePageMeta } from "~/.nuxt/imports";
import { client as clientType } from "~/utils/types/database";
import pagedTable from "~/components/pagedTable.vue";
definePageMeta({ middleware: ["auth"] });
const clientsRequest = await useFetch("/api/clients");
if (clientsRequest.error.value) throw createError(clientsRequest.error.value?.data ?? "");
const clients = clientsRequest.data as Ref<NonNullable<typeof clientsRequest.data.value>>;
const countRequest = await useFetch("/api/clients/count");
if (countRequest.error.value) throw createError(countRequest.error.value?.data ?? "");
const count = countRequest.data as Ref<NonNullable<typeof countRequest.data.value>>;
function rowClicked(client: string, edit = false) {
console.log(client);
}
async function rowDelete(client: string) {
try {
await $fetch<clientType>(`/api/clients/${client}`, {
method: "DELETE",
});
clients.value = clients.value.filter(e => e.id !== client);
count.value.count--;
} catch (e) {
// FIXME: show the error
console.log(e);
}
}
const loadingMore = ref<boolean>(false);
async function loadBefore() {
loadingMore.value = true;
clients.value.push(...await $fetch("/api/clients", {
query: {
before: clients.value[clients.value.length - 1].id,
},
}));
loadingMore.value = false;
}
</script>
<template>
<VOverlay
model-value
origin="top center"
:scrim="false"
height="fit-content"
persistent
no-click-animation
>
<VAlert class="alert">
owowowowowowowowo
</VAlert>
</VOverlay>
<VRow>
<VCol>
<VBreadcrumbs :items="['Clients']" />
<VSpacer />
<div class="text-h4">
There are {{ count?.count }} clients in the database.
</div>
</VCol>
</VRow>
<VRow>
<VCol cols="12">
<VCard>
<VTable>
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th />
</tr>
</thead>
<pagedTable
buttons
:records="clients"
:fields="['name', 'address']"
record-key="id"
@click="(id) => rowClicked(id, false)"
@click:edit="(id) => rowClicked(id, true)"
@click:delete="(id) => rowDelete(id)"
/>
</VTable>
</VCard>
<VCol>
<VBtn
v-if="clients.length < count.count"
color="primary"
:loading="loadingMore"
@click="loadBefore"
>
Load more
</VBtn>
</VCol>
</VCol>
</VRow>
</template>

28
pages/forms.vue Normal file
View file

@ -0,0 +1,28 @@
<script setup lang="ts">
/* global $fetch */
import { ref } from 'vue';
const question = ref();
const answer = ref();
async function getAnswer() {
const message = await $fetch("/api/echo", {
body: question.value,
method: "POST",
});
answer.value = message;
}
</script>
<template>
<VCard width="400px">
<template #text>
<VTextarea v-model="question" /><br>
<p>{{ answer }}</p>
<VBtn @click="getAnswer">
Send
</VBtn>
</template>
</VCard>
</template>

22
pages/index.vue Normal file
View file

@ -0,0 +1,22 @@
<script setup>
import Test from '~/components/test.vue';
</script>
<template>
<h1>Hi mom!</h1><br>
<Test />
<Test />
<Test />
<Test />
<Test /><br>
<VCard
text="..."
variant="outlined"
/>
</template>
<style scoped>
h1 {
color: blue;
}
</style>

129
pages/login.vue Normal file
View file

@ -0,0 +1,129 @@
<script setup lang="ts">
/* global $fetch */
import { ref, watch } from "vue";
import { VForm } from "vuetify/components";
import { navigateTo, useCookie, useFetch, useRoute } from "nuxt/app";
import { cookieSettings } from "~/utils/cookieSettings";
import { definePageMeta } from "~/.nuxt/imports";
const route = useRoute();
const login = ref("");
const password = ref("");
const loading = ref(false);
const error = ref<true | string>(true);
const form = ref<VForm | null>(null);
const loggedIn = ref<boolean>(useCookie("token", cookieSettings).value != null);
const redirectTo = ref(route.redirectedFrom);
definePageMeta({
layout: false,
});
async function submit() {
loading.value = true;
try {
const result = await $fetch("/api/login", {
body: { login: login.value, password: password.value },
method: "POST",
});
console.log(result);
loggedIn.value = true;
password.value = "";
} catch (e) {
console.log(typeof e);
console.error(e);
console.log(e);
error.value = (e as any).data.message;
}
loading.value = false;
if (redirectTo.value) navigateTo(redirectTo.value.fullPath);
if (form.value) form.value.validate();
}
async function logout() {
try {
await $fetch("/api/logout");
loggedIn.value = false;
} catch (e) {
console.error(e);
}
}
const userInfo = ref(JSON.stringify(useFetch("/api/users/me").data));
watch(loggedIn, updateUserInfo);
async function updateUserInfo() {
if (loggedIn.value) {
try {
userInfo.value = JSON.stringify(await $fetch("/api/users/me"));
} catch (e) {
// expected if the user is not logged in
userInfo.value = "";
}
} else {
userInfo.value = "";
}
}
updateUserInfo();
</script>
<template>
<VCard max-width="450px" class="mx-auto mt-16" variant="outlined">
<VProgressLinear
absolute
:active="loading"
:indeterminate="loading"
color="primary"
/>
<template #title>
Log in
</template>
<template #text>
<VForm v-if="!loggedIn" ref="form" class="form" :disabled="loading">
<VTextField
v-model="login"
label="Login"
:rules="[() => error]"
:disabled="loggedIn"
autocomplete="username"
variant="outlined"
color="primary"
/>
<VTextField
v-model="password"
label="Password"
type="password"
:rules="[() => error]"
:disabled="loggedIn"
autocomplete="current-password"
variant="outlined"
color="primary"
class="pt-4"
/>
</VForm>
<p v-if="loggedIn">
Logged in<br>{{ userInfo }}
</p>
</template>
<template #actions>
<VBtn
v-if="!loggedIn"
color="primary"
:disabled="loggedIn || loading"
@click="submit"
>
Login
</VBtn>
<VBtn
v-if="loggedIn"
color="primary"
:disabled="!loggedIn"
@click="logout"
>
Logout
</VBtn>
</template>
</VCard>
</template>

50
pages/tableExample.vue Normal file
View file

@ -0,0 +1,50 @@
<script setup lang="ts">
import { useFetch } from '#app';
import { ref } from 'vue';
const tableContent = ref(await useFetch("/api/dbtest").data);
</script>
<template>
<v-card width="min-content">
<v-table>
<thead>
<tr>
<th>id</th>
<th>nazwa</th>
<th>adres</th>
<th>kontakt</th>
<th />
</tr>
</thead>
<tbody>
<tr v-for="row in tableContent" :key="row.id">
<td>{{ row.id }}</td>
<td>{{ row.nazwa }}</td>
<td>{{ row.adres }}</td>
<td>{{ row.kontakt }}</td>
<td>
<v-btn-group variant="plain">
<v-btn icon="mdi-pencil" color="main" />
<v-btn icon="mdi-delete" color="red" />
</v-btn-group>
</td>
</tr>
<tr class="last">
<td>*</td>
<td><v-text-field label="nazwa" density="compact" /></td>
<td><v-text-field label="adres" density="compact" /></td>
<td><v-text-field label="kontakt" density="compact" /></td>
<td><v-btn icon="mdi-plus" variant="plain" class="rounded" /></td>
</tr>
</tbody>
</v-table>
</v-card>
</template>
<style scoped>
.last td:not(:last-child):not(:first-child) {
padding-left: 0;
padding-right: 0;
}
</style>