bug fixes, accessiblity improvements, and more

This commit is contained in:
Zoe
2024-09-12 00:43:17 -05:00
parent 1fb4c71b2b
commit cbeafb3995
20 changed files with 241 additions and 271 deletions

View File

@@ -19,7 +19,6 @@
--color-accent: 136 57 239;
--color-accent-20: #dac9f1;
--nav-height: 48px;
}
}
@@ -38,7 +37,7 @@
--color-love: 235 111 146;
--color-pine: 49 116 143;
--color-accent: 154 87 237;
--color-accent-20: #342c3f;
--color-accent-20: #2a1c3d;
color-scheme: dark;
}
@@ -48,7 +47,8 @@
@apply ease-[cubic-bezier(0.25,_1,_0.5,_1)];
}
body {
html, body {
overflow: hidden !important;
background-color: rgb(var(--color-base));
color: rgb(var(--color-text));
}

Binary file not shown.

View File

@@ -25,7 +25,8 @@ const crumbs = computed(() => {
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m9 6l6 6l-6 6" />
</svg>
<NuxtLink :class="index === crumbs.length - 1 ? 'text-foam' : 'text-subtle hover:text-text'"
<NuxtLink class="focus:outline-none focus:ring focus:ring-inset"
:class="index === crumbs.length - 1 ? 'text-foam' : 'text-subtle hover:text-text focus:text-text'"
:to="crumb.link">{{
crumb.name }}</NuxtLink>
</span>

View File

@@ -1,5 +1,6 @@
<template>
<div v-on:click="toggle()" class="w-5 h-5 border rounded cursor-pointer flex items-center justify-center"
<div v-on:click="toggle()" v-on:keypress.enter="toggle()" v-on:keypress.space="toggle()" tabindex="0"
class="w-5 h-5 border rounded cursor-pointer flex items-center justify-center focus:outline-none focus:ring focus:ring-inset"
:class="state === 'unchecked' ? 'hover:bg-muted/5 active:bg-muted/15' : 'bg-accent/10 hover:bg-accent/15 active:bg-accent/25 text-accent'">
<div v-if="state === 'some'" class="w-8/12 h-0.5 bg-current rounded-full"></div>
<span v-else-if="state === 'checked'">

View File

@@ -3,7 +3,7 @@ import { useUser } from '~/composables/useUser'
const { getUser } = useUser()
const props = defineProps({
usageBytes: Number
usageBytes: Number,
})
const user = await getUser()
@@ -35,7 +35,11 @@ const isInFolder = computed(() => route.path.startsWith('/home/') && route.path
</script>
<template>
<aside class="h-screen flex flex-col w-56 pt-3 bg-surface border-r">
<aside class="h-screen flex flex-col w-56 pt-3 bg-surface border-r z-50 md:z-20">
<a href="#main"
class="absolute w-fit -translate-x-full top-0 px-2 py-4 bg-surface border opacity-0 focus-within:translate-x-0 focus-within:opacity-100">
Skip to content
</a>
<div class="pl-9 h-14 flex items-center">
<h2>Home</h2>
</div>
@@ -43,7 +47,7 @@ const isInFolder = computed(() => route.path.startsWith('/home/') && route.path
<ul class="flex flex-col gap-y-2">
<li>
<NuxtLink to="/home"
class="flex py-1.5 px-4 rounded-lg transition-bg duration-300 hover:bg-muted/10"
class="flex py-1.5 px-4 rounded-lg transition-bg duration-300 hover:bg-muted/10 focus:outline-none focus:ring focus:ring-inset"
:class="{ 'bg-muted/10': isAllFilesActive }">
<div class="flex relative">
<svg class="m-0.5 mr-2" xmlns="http://www.w3.org/2000/svg" width="20" height="20"

View File

@@ -1,69 +0,0 @@
<script setup lang="ts">
let colorMode = useColorMode();
const changeTheme = () => {
if (colorMode.preference === "dark") {
// from dark => light
colorMode.preference = "light"
} else if (colorMode.preference === "light") {
// from light => system
colorMode.preference = "system";
} else {
// from system => dark
colorMode.preference = "dark";
}
return;
}
</script>
<template>
<header class="flex h-[var(--nav-height)] px-4 justify-center sticky top-0 z-20 border-b bg-base">
<div class="flex w-full items-center justify-between space-x-2.5">
<p
class="-ml-2.5 flex shrink-0 items-center px-2.5 py-1.5 focus:outline-none focus:ring rounded-m font-semiboldd">
filething
</p>
</div>
<nav class="hidden md:flex" aria-label="Main">
<ul class="flex items-center gap-3" role="list">
<li>
<a href="#"
class="px-2.5 py-1.5 text-[15px] font-semibold transition-bg duration-300 hover:bg-muted/10 focus:outline-none focus:ring focus:ring-inset rounded-md">Link</a>
</li>
<li>
<a href="#"
class="px-2.5 py-1.5 text-[15px] font-semibold transition-bg duration-300 hover:bg-muted/10 focus:outline-none focus:ring focus:ring-inset rounded-md">Link</a>
</li>
<li>
<a href="#"
class="px-2.5 py-1.5 text-[15px] font-semibold transition-bg duration-300 hover:bg-muted/10 focus:outline-none focus:ring focus:ring-inset rounded-md">Link</a>
</li>
<li class="h-6 border-r"></li>
<li>
<button
class="flex items-center px-3 h-8 text-[15px] font-semibold transition-bg duration-300 hover:bg-muted/10 focus:outline-none focus:ring focus:ring-inset rounded-md"
@click="changeTheme">
<span class="inline-block">
<svg v-if="$colorMode.preference === 'dark'" xmlns="http://www.w3.org/2000/svg" width="22"
height="22" viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M12 3h.393a7.5 7.5 0 0 0 7.92 12.446A9 9 0 1 1 12 2.992z" />
</svg>
<svg v-else-if="$colorMode.preference === 'light'" xmlns="http://www.w3.org/2000/svg"
width="22" height="22" viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2"
d="M14.828 14.828a4 4 0 1 0-5.656-5.656a4 4 0 0 0 5.656 5.656m-8.485 2.829l-1.414 1.414M6.343 6.343L4.929 4.929m12.728 1.414l1.414-1.414m-1.414 12.728l1.414 1.414M4 12H2m10-8V2m8 10h2m-10 8v2" />
</svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 256 256">
<path fill="currentColor"
d="M208 36H48a28 28 0 0 0-28 28v112a28 28 0 0 0 28 28h160a28 28 0 0 0 28-28V64a28 28 0 0 0-28-28Zm4 140a4 4 0 0 1-4 4H48a4 4 0 0 1-4-4V64a4 4 0 0 1 4-4h160a4 4 0 0 1 4 4Zm-40 52a12 12 0 0 1-12 12H96a12 12 0 0 1 0-24h64a12 12 0 0 1 12 12Z" />
</svg>
</span>
</button>
</li>
</ul>
</nav>
</header>
</template>

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { formatBytes } from '~/utils/formatBytes';
import type { FileUpload } from '~/types/user';
import type { FileUpload } from '~/types/file';
const props = defineProps({
uploadingFiles: {
@@ -45,12 +45,21 @@ const formatRemainingTime = (seconds: number): string => {
const truncateFilenameToFitWidth = (filename: string, maxWidthPx: number, font = '18px ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji') => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
if (context === null) {
return
}
context.font = font;
const name = filename.substring(0, filename.lastIndexOf('.'));
const extension = filename.substring(filename.lastIndexOf('.'));
function getTextWidth(text) {
function getTextWidth(text: string): number {
if (context === null) {
return 0
}
return context.measureText(text).width;
}
@@ -99,7 +108,7 @@ let uploadFailed = computed(() => props.uploadingFiles.filter(x => x.status.erro
</script>
<template>
<div class="absolute bottom-0 right-0 m-3 rounded-2xl border flex flex-col sm:w-[440px] w-[calc(100%-24px)] shadow-md bg-surface"
<div class="absolute bottom-0 right-0 m-3 rounded-2xl border flex flex-col sm:w-[440px] w-[calc(100%-24px)] shadow-md bg-surface z-20"
:class="{ 'h-[510px]': !collapsed, 'hidden': closed }">
<div class="flex flex-row justify-between h-14 items-center mb-3 px-4" :class="{ 'hidden': collapsed }">
<h3 class="text-xl font-semibold">Upload</h3>

View File

@@ -1,22 +1,22 @@
{
"name": "nuxt-app",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@nuxtjs/color-mode": "^3.4.4",
"nuxt": "^3.13.1",
"vue": "latest"
},
"devDependencies": {
"autoprefixer": "^10.4.20",
"postcss": "^8.4.45",
"tailwindcss": "^3.4.10"
}
}
"name": "nuxt-app",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@nuxtjs/color-mode": "^3.4.4",
"nuxt": "^3.13.1",
"vue": "latest"
},
"devDependencies": {
"autoprefixer": "^10.4.20",
"postcss": "^8.4.45",
"tailwindcss": "^3.4.10"
}
}

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import type { NuxtError } from '#app';
import { useUser } from '~/composables/useUser'
import type { File } from '~/types/file';
import type { FileUpload } from '~/types/user';
import type { File, UploadResponse, FileUpload } from '~/types/file';
const { getUser } = useUser()
definePageMeta({
@@ -11,7 +11,7 @@ definePageMeta({
const user = await getUser()
const route = useRoute();
let { data: files } = await useFetch<[File]>('/api/files/get/' + route.path.replace(/^\/home/, ''))
let { data: files } = await useFetch<File[]>('/api/files/get/' + route.path.replace(/^\/home/, ''))
const sortedFiles = computed(() => {
files.value?.forEach(file => file.toggled === undefined ? file.toggled = 'unchecked' : {})
@@ -23,16 +23,20 @@ const sortedFiles = computed(() => {
return ('' + a.name).localeCompare(b.name);
});
return folders?.concat(archives)
if (folders === undefined || archives === undefined) {
return
}
return folders.concat(archives)
})
let selectAll = ref('unchecked');
let selectAll: Ref<"unchecked" | "some" | "checked"> = ref('unchecked');
let selectedFiles = computed(() => sortedFiles.value?.filter(file => file.toggled === 'checked'))
watch(sortedFiles, (newVal, oldVal) => {
let checkedFilesLength = newVal?.filter(file => file.toggled === 'checked').length;
if (checkedFilesLength > 0) {
if (checkedFilesLength < newVal?.length) {
if (newVal !== undefined && checkedFilesLength !== undefined && checkedFilesLength > 0) {
if (checkedFilesLength < newVal.length) {
selectAll.value = 'some';
} else {
selectAll.value = 'checked';
@@ -58,6 +62,8 @@ let folderError = ref('');
let popupVisable = ref(false);
let uploadPaneClosed = ref(true);
let fileNavClosed = ref(true);
if (typeof route.params.name == "object") {
folder.value = route.params.name.join("/");
}
@@ -82,12 +88,12 @@ const handleFileChange = (event: Event) => {
return
}
if (fileInput.value.files.length > 0) {
if (fileInput.value.files && fileInput.value.files.length > 0) {
fileInput.value.value = "";
}
}
const uploadFile = (file: File) => {
const uploadFile = (file: globalThis.File) => {
const xhr = new XMLHttpRequest();
const startTime = Date.now();
let id = `${file.name}-${Math.floor(Math.random() * 1000)}`;
@@ -97,7 +103,15 @@ const uploadFile = (file: File) => {
uploading: true,
controller: xhr,
startTime,
file: file,
speed: 0,
remainingTime: Infinity,
file: {
name: file.name,
is_dir: false,
size: file.size,
last_modified: "",
toggled: "unchecked"
},
length: {},
status: {}
}
@@ -208,7 +222,7 @@ const openFilePicker = () => {
}
const createFolder = async () => {
const { data, error } = await useAsyncData(
const { data, error } = await useAsyncData<UploadResponse, NuxtError<{ message: string }>>(
() => $fetch('/api/files/upload' + route.path.replace(/^\/home/, '') + '/' + folderName.value, {
method: "POST",
body: {
@@ -217,14 +231,16 @@ const createFolder = async () => {
})
)
if (error.value != null) {
folderError.value = error.value.data.message;
} else {
console.log(error.value)
if (data.value != null) {
user.usage = data.value.usage
files.value?.push(data.value.file)
popupVisable.value = false;
navigateTo(route.path + '/' + folderName.value);
} else if (error.value != null && error.value.data != undefined) {
folderError.value = error.value.data.message;
}
}
@@ -236,10 +252,14 @@ const deleteFiles = async () => {
}
})
if (files.value === null) {
throw new Error("Files are null!")
}
files.value = files.value?.filter(file => !selectedFiles.value?.includes(file))
}
const downloadFile = (file) => {
const downloadFile = (file: File) => {
const anchor = document.createElement('a');
anchor.href = '/api/files/download/' + file.name;
anchor.download = file.name;
@@ -253,13 +273,17 @@ const downloadFiles = async () => {
let filenames = ""
selectedFiles.value?.forEach((file, i) => {
if (selectedFiles.value === undefined) {
throw new Error("selected files is undefined")
}
filenames += encodeURIComponent(file.name)
if (i != selectedFiles.value?.length - 1) {
if (i != selectedFiles.value.length - 1) {
filenames += ",";
}
})
let { data, error } = await useAsyncData(
let { data, error } = await useAsyncData<Blob, NuxtError<{ message: string }>>(
() => $fetch('/api/files/download', {
params: {
"filenames": filenames
@@ -267,9 +291,7 @@ const downloadFiles = async () => {
})
)
console.log("DATA", data.value)
if (error.value == null) {
if (data.value !== null) {
const anchor = document.createElement('a');
anchor.href = window.URL.createObjectURL(data.value)
anchor.download = "filething.zip";
@@ -283,7 +305,11 @@ const downloadFiles = async () => {
<template>
<div class="flex relative min-h-[100dvh]">
<div class="fixed md:relative -translate-x-full md:translate-x-0">
<div v-if="!fileNavClosed" v-on:click="fileNavClosed = !fileNavClosed"
class="absolute top-0 left-0 bottom-0 right-0 bg-base/40 z-40 block md:hidden">
</div>
<div class="fixed md:relative -translate-x-full md:translate-x-0 transition-transform z-50 md:z-20"
:class="{ 'translate-x-0': !fileNavClosed }">
<FileNav :usageBytes="user.usage" />
</div>
<UploadPane :closed="uploadPaneClosed" v-on:update:closed="(newValue) => uploadPaneClosed = newValue"
@@ -306,13 +332,13 @@ const downloadFiles = async () => {
</Popup>
<div class="w-full">
<Nav />
<div class="pt-6 pl-12 overflow-auto max-h-[calc(100vh-var(--nav-height))]">
<Nav v-on:update:filenav="(e) => fileNavClosed = e" :filenav="fileNavClosed" />
<div class="pt-6 pl-12 overflow-y-auto max-h-[calc(100vh-var(--nav-height))]" id="main">
<div class="flex gap-x-4 flex-col">
<div class="py-5 flex flex-row gap-x-4">
<input type="file" ref="fileInput" @change="handleFileChange" multiple class="hidden" />
<button v-on:click="openFilePicker"
class="rounded-xl border-2 border-surface flex flex-col gap-y-2 px-2 py-3 w-40 justify-center items-center hover:bg-muted/10 active:bg-muted/20 transition-bg">
class="focus:outline-none focus:ring focus:ring-inset rounded-xl border-2 border-surface flex flex-col gap-y-2 px-2 py-3 w-40 justify-center items-center hover:bg-muted/10 active:bg-muted/20 transition-bg">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
@@ -323,7 +349,7 @@ const downloadFiles = async () => {
Upload
</button>
<button v-on:click="popupVisable = !popupVisable"
class="rounded-xl border-2 border-surface flex flex-col gap-y-2 px-2 py-3 w-40 justify-center items-center hover:bg-muted/10 active:bg-muted/20 transition-bg">
class="focus:outline-none focus:ring focus:ring-inset rounded-xl border-2 border-surface flex flex-col gap-y-2 px-2 py-3 w-40 justify-center items-center hover:bg-muted/10 active:bg-muted/20 transition-bg">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
@@ -342,9 +368,10 @@ const downloadFiles = async () => {
<Breadcrumbs :path="route.path" />
</h3>
<div class="mt-2">
<div class="flex flex-row gap-x-2" v-if="selectedFiles?.length > 0">
<div class="flex flex-row gap-x-2"
v-if="selectedFiles !== undefined && selectedFiles.length > 0">
<button v-on:click="downloadFiles"
class="flex flex-row px-2 py-1 rounded-md transition-bg text-xs border hover:bg-muted/10 active:bg-muted/20 items-center">
class="flex flex-row px-2 py-1 rounded-md transition-bg text-xs border hover:bg-muted/10 active:bg-muted/20 items-center focus:outline-none focus:ring focus:ring-inset">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2"
@@ -353,7 +380,7 @@ const downloadFiles = async () => {
Download
</button>
<button v-on:click="deleteFiles"
class="flex flex-row px-2 py-1 rounded-md transition-bg text-xs border hover:bg-love/10 active:bg-love/20 hover:text-love active:text-love items-center">
class="flex flex-row px-2 py-1 rounded-md transition-bg text-xs border hover:bg-love/10 active:bg-love/20 hover:text-love active:text-love items-center focus:outline-none focus:ring focus:ring-inset">
<svg class="mr-1" xmlns="http://www.w3.org/2000/svg" width="16" height="16"
viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round"
@@ -389,7 +416,7 @@ const downloadFiles = async () => {
<tr class="flex border-l-2 flex-row h-10 group items-center border-b active:bg-surface/45 transition-bg relative"
v-for="file in sortedFiles"
:class="file.toggled === 'checked' ? 'bg-accent/20 border-l-accent' : 'border-l-transparent hover:bg-surface'">
<td class="-ml-7 pr-4 flex-shrink-0">
<td class="-ml-7 flex-shrink-0">
<div class="w-5 h-5">
<Checkbox class="group-hover:flex"
:class="{ 'hidden': file.toggled === 'unchecked' }"
@@ -397,7 +424,10 @@ const downloadFiles = async () => {
</div>
</td>
<td v-on:click="file.toggled === 'unchecked' ? file.toggled = 'checked' : file.toggled = 'unchecked'"
class="flex-grow text-start flex items-center h-full min-w-40">
v-on:keypress.enter="file.toggled === 'unchecked' ? file.toggled = 'checked' : file.toggled = 'unchecked'"
v-on:keypress.space="file.toggled === 'unchecked' ? file.toggled = 'checked' : file.toggled = 'unchecked'"
class="flex-grow text-start flex items-center h-full min-w-40 focus:outline-none focus:ring focus:ring-inset pl-4"
tabindex="0">
<div class="flex items-center min-w-40">
<svg v-if="!file.is_dir" class="mr-2 flex-shrink-0"
xmlns="http://www.w3.org/2000/svg" width="16" height="16"
@@ -416,7 +446,8 @@ const downloadFiles = async () => {
d="M5 4h4l3 3h7a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2" />
</svg>
<span class="overflow-hidden whitespace-nowrap text-ellipsis">
<NuxtLink v-if="file.is_dir" :to="`${route.path}/${file.name}`">
<NuxtLink v-if="file.is_dir" class="hover:underline focus:underline"
:to="`${route.path}/${file.name}`">
{{ file.name }}
</NuxtLink>
<span v-else>{{ file.name }}</span>
@@ -430,9 +461,9 @@ const downloadFiles = async () => {
{{ file.last_modified }}
</td>
<td :class="file.toggled === 'checked' ? 'context-active' : 'context'"
class="absolute pl-6 top-0 bottom-0 right-0 hidden group-hover:flex items-center pr-8">
class="absolute pl-6 top-0 bottom-0 right-0 hidden group-hover:flex group-focus-within:flex items-center pr-8">
<button v-on:click="downloadFile(file)"
class="p-2 rounded hover:bg-muted/10 active:bg-muted/20">
class="p-2 rounded hover:bg-muted/10 active:bg-muted/20 focus:outline-none focus:ring focus:ring-inset">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"
viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round"
@@ -464,6 +495,6 @@ th {
}
.context {
background: linear-gradient(to right, transparent, rgb(var(--color-surface)) 16px, rgb(var(--color-surface)) 100%);
background: linear-gradient(to right, transparent, rgb(var(--color-base)) 16px, rgb(var(--color-base)) 100%);
}
</style>

View File

@@ -1,6 +1,7 @@
<script lang="ts" setup>
import type { NuxtError } from '#app';
import type { User } from '~/types/user';
const { fetchUser } = useUser()
const { setUser } = useUser()
definePageMeta({
middleware: "unauth"
@@ -12,19 +13,21 @@ let password = ref('')
let error = ref('')
const submitForm = async () => {
let response = await useFetch<User>('/api/login', {
method: 'POST',
body: {
"username_or_email": username_or_email.value,
"password": password.value,
}
})
let { data, error: fetchError } = await useAsyncData<User, NuxtError<{ message: string }>>(
() => $fetch('/api/login', {
method: 'POST',
body: {
"username_or_email": username_or_email.value,
"password": password.value,
}
})
)
if (response.error.value != null) {
error.value = response.error.value.data.message
if (fetchError.value !== null && fetchError.value.data !== undefined) {
error.value = fetchError.value.data.message
setTimeout(() => error.value = "", 15000)
} else {
await fetchUser()
} else if (data.value !== null) {
setUser(data.value)
await navigateTo('/home')
}
}
@@ -38,8 +41,9 @@ const submitForm = async () => {
<Input v-model="password" type="password" placeholder="Password..." />
<p class="text-love">{{ error }}</p>
<button @click="submitForm"
class="py-2 px-4 my-2 bg-pine/10 text-pine rounded-md transition-colors hover:bg-pine/15 active:bg-pine/25">Login</button>
<p>Or <NuxtLink to="/signup" class="text-foam hover:underline">Sign up</NuxtLink>
class="py-2 px-4 my-2 bg-pine/10 text-pine rounded-md transition-colors hover:bg-pine/15 active:bg-pine/25 focus:outline-none focus:ring focus:ring-inset">Login</button>
<p>Or <NuxtLink to="/signup"
class="text-foam hover:underline focus:outline-none focus:ring focus:ring-inset">Sign up</NuxtLink>
</p>
</div>
</div>

View File

@@ -1,6 +1,7 @@
<script lang="ts" setup>
import type { NuxtError } from '#app';
import type { User } from '~/types/user'
const { fetchUser } = useUser()
const { setUser } = useUser()
definePageMeta({
middleware: "unauth"
@@ -13,20 +14,22 @@ let password = ref('')
let error = ref('')
const submitForm = async () => {
const response = await useFetch<User>('/api/signup', {
method: 'POST',
body: {
"username": username.value,
"email": email.value,
"password": password.value,
}
})
let { data, error: fetchError } = await useAsyncData<User, NuxtError<{ message: string }>>(
() => $fetch('/api/signup', {
method: 'POST',
body: {
"username": username.value,
"email": email.value,
"password": password.value,
}
})
)
if (response.error.value != null) {
error.value = response.error.value.data.message
if (fetchError.value != null && fetchError.value.data !== undefined) {
error.value = fetchError.value.data.message
setTimeout(() => error.value = "", 15000)
} else {
await fetchUser()
} else if (data.value !== null) {
setUser(data.value)
await navigateTo('/home')
}
}
@@ -42,8 +45,9 @@ const submitForm = async () => {
<Input v-model="password" type="password" placeholder="Password..." />
<p class="text-love">{{ error }}</p>
<button @click="submitForm"
class="py-2 px-4 my-2 bg-pine/10 text-pine rounded-md transition-colors hover:bg-pine/15 active:bg-pine/25">Login</button>
<p>Or <NuxtLink to="/login" class="text-foam hover:underline">Log in</NuxtLink>
class="py-2 px-4 my-2 bg-pine/10 text-pine rounded-md transition-colors hover:bg-pine/15 active:bg-pine/25 focus:outline-none focus:ring focus:ring-inset">Login</button>
<p>Or <NuxtLink to="/login"
class="text-foam hover:underline focus:outline-none focus:ring focus:ring-inset">Log in</NuxtLink>
</p>
</div>
</div>

View File

@@ -3,5 +3,30 @@ export interface File {
is_dir: boolean,
size: number,
last_modified: string,
toggled: string,
toggled: "checked" | "some" | "unchecked",
}
export interface FileUpload {
id: string,
uploading: boolean,
file: File,
startTime: number,
speed: number,
remainingTime: number,
controller: XMLHttpRequest,
length: {
total: number,
loaded: number,
} | {},
status: {
error: boolean,
aborted: boolean,
code: number,
message: string
} | {},
}
export interface UploadResponse {
usage: number,
file: File
}

View File

@@ -8,23 +8,3 @@ export interface User {
},
usage: number,
}
export interface FileUpload {
id: string,
uploading: boolean,
file: File,
startTime: number,
speed: number,
remainingTime: number,
controller: XMLHttpRequest,
length: {
total: number,
loaded: number,
},
status: {
error: boolean,
aborted: boolean,
code: number,
message: string
},
}