Misc changes

This commit is contained in:
lucast committed 2026-02-02 16:07:24 +01:00
1 parent 5c44febc65
commit 1ef0cfb419
7 files changed
+147 -9

No files matched your search

+85
View File
@@ -0,0 +1,85 @@
<script setup lang="ts">
import { type Option } from "~/ui/SearchSelect.vue"
type InputType = "text" | "file" | "search-select"
interface BaseField<T> {
key: keyof T
label?: string
}
interface TextField<T> extends BaseField<T> {
type?: "text"
}
interface FileField<T> extends BaseField<T> {
type: "file"
}
interface SearchSelectField<T> extends BaseField<T> {
type: "search-select"
fetchOptions: (q: string) => Promise<Option[]>
}
export type Field<T = any> = TextField<T> | FileField<T> | SearchSelectField<T>
interface ModalFormProps<T> {
item?: T
fields: Field<T>[]
title?: string
}
const props = defineProps<ModalFormProps<any>>()
const open = defineModel("open", { required: true })
const emit = defineEmits<{ (e: "save", value: any): boolean }>()
let localItem = reactive<any>({})
const save = () => {
if (emit("save", localItem)) open.value = false
}
</script>
<template>
<Modal v-model:open="open" width="40%">
<p v-if="props.title" class="title">{{ props.title }}</p>
<form class="form" @submit.prevent="save">
<template v-for="field in props.fields">
<SearchSelect
v-if="field.type == 'search-select'"
v-model:value="localItem[field.key]"
:fetch-options="field.fetchOptions"
:label="field.label || field.key"
:predefined="props.item?.[field.key]"
/>
<Input
v-else
:type="field.type"
:key="field.key"
:label="field.label || field.key"
v-model:value="localItem[field.key]"
full
/>
</template>
<Button type="submit" full center-text>Save</Button>
</form>
</Modal>
</template>
<style lang="scss" scoped>
@use "@/styles/util" as *;
.title {
@include fontSize(20px);
font-weight: 600;
margin-bottom: 0.5rem;
}
.form {
display: flex;
flex-direction: column;
gap: 0.8rem;
}
</style>
File renamed without changes.
+3 -6
View File
@@ -3,9 +3,9 @@ import defu from "defu"
export function useApiFetch<DataT = any, ErrorT = any>(
url: string | Request | Ref<string | Request> | (() => string | Request),
options: UseFetchOptions<DataT, ErrorT> = {},
options?: UseFetchOptions<DataT, ErrorT>,
authorize: boolean = true
) {
): ReturnType<typeof useFetch<DataT, ErrorT>> {
const baseUrl = import.meta.env.VITE_PUBLIC_BACKEND
if (!baseUrl) throw new Error("BACKEND URL not set")
@@ -39,10 +39,7 @@ export function useApiFetch<DataT = any, ErrorT = any>(
},
}
const config = defu(options || {}, defaults) as UseFetchOptions<
DataT,
ErrorT
>
const config: UseFetchOptions<DataT, ErrorT> = defu(options ?? {}, defaults)
// @ts-ignore
return useFetch<DataT, ErrorT>(url, config)
+3 -1
View File
@@ -1,11 +1,13 @@
<script setup lang="ts">
const props = defineProps<{ width?: string }>()
const open = defineModel("open")
</script>
<template>
<Transition>
<div class="modal-container" v-show="open" @click="open = false">
<Card @click.stop>
<Card @click.stop :width="props.width">
<slot />
</Card>
</div>
+19 -1
View File
@@ -5,7 +5,9 @@ export interface Option {
}
interface SearchSelectProps {
label?: string
fetchOptions: (q: string) => Promise<Option[]>
predefined?: Option
}
const props = defineProps<SearchSelectProps>()
@@ -15,6 +17,16 @@ const query = ref<string>("")
const options = ref<Option[]>([])
const show = ref<boolean>(false)
watchEffect(() => {
if (!props.predefined) return
console.log(props.predefined)
query.value = props.predefined.label
value.value = props.predefined.value
nextTick(() => (options.value = []))
})
watch(query, async (q) => {
options.value = q ? await props.fetchOptions(q) : []
show.value = true
@@ -29,7 +41,13 @@ const select = (item: Option) => {
<template>
<div class="search-select" @blur="show = false">
<Input v-model:value="query" @focus="show = true" type="text" full />
<Input
v-model:value="query"
@focus="show = true"
:label="props.label"
type="text"
full
/>
<ul v-if="options.length && show">
<li v-for="opt in options" :key="opt.value" @click="select(opt)">
+26 -1
View File
@@ -16,7 +16,7 @@ const { rows, data } = defineProps<TableProps>()
</script>
<template>
<table>
<table class="table">
<thead>
<tr>
<th
@@ -40,3 +40,28 @@ const { rows, data } = defineProps<TableProps>()
</tbody>
</table>
</template>
<style lang="scss" scoped>
@use "@/styles/variables" as *;
@use "@/styles/util" as *;
.table {
width: 100%;
text-align: left;
border-collapse: collapse;
@include fontSize(15px);
& th,
& td {
border-bottom: 1px solid $color-border;
padding: 0.5rem 0.6rem;
}
& tr:last-child td {
border-bottom: none;
}
& thead {
}
}
</style>
+11
View File
@@ -17,3 +17,14 @@ export interface Artist extends Base {
export interface Genre extends Base {
name: string
}
export interface Track extends Base {
name: string
file: string
lyrics: {
synced: boolean
text: string
}
album: Album
artists: Artist[]
}