Add orval and backend integration
This commit is contained in:
75
app/components/internal/PlaylistSelect.vue
Normal file
75
app/components/internal/PlaylistSelect.vue
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ChevronsUpDown, Music4 } from 'lucide-vue-next';
|
||||||
|
import Frame from '@/components/ui/frame/Frame.vue';
|
||||||
|
import Select from '@/components/ui/select/Select.vue';
|
||||||
|
import SelectCustomTrigger from '@/components/ui/select/SelectCustomTrigger.vue';
|
||||||
|
import { useSidebar } from '@/components/ui/sidebar';
|
||||||
|
import SelectContent from '@/components/ui/select/SelectContent.vue';
|
||||||
|
import SelectLabel from '@/components/ui/select/SelectLabel.vue';
|
||||||
|
import SelectSeparator from '@/components/ui/select/SelectSeparator.vue';
|
||||||
|
import SelectGroup from '@/components/ui/select/SelectGroup.vue';
|
||||||
|
import SelectItem from '@/components/ui/select/SelectItem.vue';
|
||||||
|
import { usePlaylists } from '@/composeables/api/playlist-controller/playlist-controller';
|
||||||
|
|
||||||
|
const {
|
||||||
|
open: sidebarOpen,
|
||||||
|
} = useSidebar()
|
||||||
|
|
||||||
|
const { isLoading, isError, error, data } = usePlaylists();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Select>
|
||||||
|
<div v-if="sidebarOpen" class="hover:bg-sidebar-muted cursor-pointer rounded-md select-none">
|
||||||
|
<SelectCustomTrigger class="w-full flex p-2 gap-2 items-center">
|
||||||
|
<Frame borderRadius="round" background="primary" padding="dense" margin="none">
|
||||||
|
<Music4 class="text-primary-foreground" :size="24" />
|
||||||
|
</Frame>
|
||||||
|
<div class="overflow-hidden text-start">
|
||||||
|
<h4 class="text-xl font-semibold tracking-tight truncate">
|
||||||
|
My playlist
|
||||||
|
</h4>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
11 track(s)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ml-auto">
|
||||||
|
<ChevronsUpDown />
|
||||||
|
</div>
|
||||||
|
</SelectCustomTrigger>
|
||||||
|
</div>
|
||||||
|
<div v-if="!sidebarOpen">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<SelectCustomTrigger as-child>
|
||||||
|
<Frame borderRadius="round" background="primary" padding="dense" margin="none">
|
||||||
|
<Music4 class="text-primary-foreground" :size="24" />
|
||||||
|
</Frame>
|
||||||
|
</SelectCustomTrigger>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SelectContent class="w-full" v-if="isLoading">
|
||||||
|
<UiSpinner />
|
||||||
|
</SelectContent>
|
||||||
|
<SelectContent class="w-full" v-else-if="isError">
|
||||||
|
<SelectLabel>{{ error?.message }}</SelectLabel>
|
||||||
|
</SelectContent>
|
||||||
|
<SelectContent class="w-full" v-else-if="data">
|
||||||
|
<SelectLabel>Playlists</SelectLabel>
|
||||||
|
<SelectSeparator />
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectItem value="test">
|
||||||
|
<span>Test</span>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="second">
|
||||||
|
<span>Second</span>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="third">
|
||||||
|
<span>Third</span>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="fourth">
|
||||||
|
<span>Fourth</span>
|
||||||
|
</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</template>
|
||||||
@ -11,17 +11,8 @@ import {
|
|||||||
SidebarHeader,
|
SidebarHeader,
|
||||||
SidebarRail,
|
SidebarRail,
|
||||||
} from "@/components/ui/sidebar"
|
} from "@/components/ui/sidebar"
|
||||||
import { useSidebar } from "@/components/ui/sidebar";
|
|
||||||
import EditAudio from "@/components/icon/EditAudio.vue";
|
import EditAudio from "@/components/icon/EditAudio.vue";
|
||||||
import Frame from "@/components/ui/frame/Frame.vue";
|
import PlaylistSelect from "@/components/internal/PlaylistSelect.vue";
|
||||||
import Select from "@/components/ui/select/Select.vue";
|
|
||||||
import SelectTrigger from "@/components/ui/select/SelectTrigger.vue";
|
|
||||||
import SelectContent from "@/components/ui/select/SelectContent.vue";
|
|
||||||
import SelectLabel from "@/components/ui/select/SelectLabel.vue";
|
|
||||||
import SelectSeparator from "@/components/ui/select/SelectSeparator.vue";
|
|
||||||
import SelectGroup from "@/components/ui/select/SelectGroup.vue";
|
|
||||||
import SelectItem from "@/components/ui/select/SelectItem.vue";
|
|
||||||
import SelectCustomTrigger from "@/components/ui/select/SelectCustomTrigger.vue";
|
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
@ -50,62 +41,13 @@ const items = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const {
|
|
||||||
open,
|
|
||||||
} = useSidebar()
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Sidebar collapsible="icon">
|
<Sidebar collapsible="icon">
|
||||||
<SidebarHeader>
|
<SidebarHeader>
|
||||||
<Select>
|
<PlaylistSelect />
|
||||||
<div v-if="open" class="hover:bg-sidebar-muted cursor-pointer rounded-md select-none">
|
|
||||||
<SelectCustomTrigger class="w-full flex p-2 gap-2 items-center">
|
|
||||||
<Frame borderRadius="round" background="primary" padding="dense" margin="none">
|
|
||||||
<Music4 class="text-primary-foreground" :size="24" />
|
|
||||||
</Frame>
|
|
||||||
<div class="overflow-hidden text-start">
|
|
||||||
<h4 class="text-xl font-semibold tracking-tight truncate">
|
|
||||||
My playlist
|
|
||||||
</h4>
|
|
||||||
<p class="text-sm text-muted-foreground">
|
|
||||||
11 track(s)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="ml-auto">
|
|
||||||
<ChevronsUpDown />
|
|
||||||
</div>
|
|
||||||
</SelectCustomTrigger>
|
|
||||||
</div>
|
|
||||||
<div v-if="!open">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<SelectCustomTrigger as-child>
|
|
||||||
<Frame borderRadius="round" background="primary" padding="dense" margin="none">
|
|
||||||
<Music4 class="text-primary-foreground" :size="24" />
|
|
||||||
</Frame>
|
|
||||||
</SelectCustomTrigger>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<SelectContent class="w-full">
|
|
||||||
<SelectLabel>Playlists</SelectLabel>
|
|
||||||
<SelectSeparator />
|
|
||||||
<SelectGroup>
|
|
||||||
<SelectItem value="test">
|
|
||||||
<span>Test</span>
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="second">
|
|
||||||
<span>Second</span>
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="third">
|
|
||||||
<span>Third</span>
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="fourth">
|
|
||||||
<span>Fourth</span>
|
|
||||||
</SelectItem>
|
|
||||||
</SelectGroup>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<SidebarGroup>
|
<SidebarGroup>
|
||||||
|
|||||||
17
app/components/ui/spinner/Spinner.vue
Normal file
17
app/components/ui/spinner/Spinner.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from "vue"
|
||||||
|
import { Loader2Icon } from "lucide-vue-next"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes["class"]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Loader2Icon
|
||||||
|
role="status"
|
||||||
|
aria-label="Loading"
|
||||||
|
:class="cn('size-4 animate-spin', props.class)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
1
app/components/ui/spinner/index.ts
Normal file
1
app/components/ui/spinner/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Spinner } from "./Spinner.vue"
|
||||||
22
app/composeables/api/axios-instance.ts
Normal file
22
app/composeables/api/axios-instance.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Axios, { type AxiosRequestConfig, type AxiosResponse } from 'axios';
|
||||||
|
export const AXIOS_INSTANCE = Axios.create();
|
||||||
|
|
||||||
|
export const axiosInstance = <T>(
|
||||||
|
config: AxiosRequestConfig,
|
||||||
|
options?: AxiosRequestConfig,
|
||||||
|
): Promise<AxiosResponse<T, any>> => {
|
||||||
|
const baseURL = useRuntimeConfig().public.apiBaseUrl;
|
||||||
|
console.log(baseURL)
|
||||||
|
const source = Axios.CancelToken.source();
|
||||||
|
const promise = AXIOS_INSTANCE({
|
||||||
|
...config,
|
||||||
|
...{
|
||||||
|
...options,
|
||||||
|
baseURL: baseURL
|
||||||
|
},
|
||||||
|
cancelToken: source.token,
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
11
app/composeables/api/models/index.ts
Normal file
11
app/composeables/api/models/index.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v7.16.0 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* OpenAPI definition
|
||||||
|
* OpenAPI spec version: v0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './playlistCreateDTO';
|
||||||
|
export * from './playlistReadDTO';
|
||||||
|
export * from './readParams';
|
||||||
|
export * from './uploadBody';
|
||||||
10
app/composeables/api/models/playlistCreateDTO.ts
Normal file
10
app/composeables/api/models/playlistCreateDTO.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v7.16.0 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* OpenAPI definition
|
||||||
|
* OpenAPI spec version: v0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface PlaylistCreateDTO {
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
14
app/composeables/api/models/playlistReadDTO.ts
Normal file
14
app/composeables/api/models/playlistReadDTO.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v7.16.0 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* OpenAPI definition
|
||||||
|
* OpenAPI spec version: v0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface PlaylistReadDTO {
|
||||||
|
id?: number;
|
||||||
|
ownerId?: number;
|
||||||
|
title?: string;
|
||||||
|
createdAt?: string;
|
||||||
|
updatedAt?: string;
|
||||||
|
}
|
||||||
10
app/composeables/api/models/readParams.ts
Normal file
10
app/composeables/api/models/readParams.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v7.16.0 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* OpenAPI definition
|
||||||
|
* OpenAPI spec version: v0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type ReadParams = {
|
||||||
|
document: string;
|
||||||
|
};
|
||||||
10
app/composeables/api/models/uploadBody.ts
Normal file
10
app/composeables/api/models/uploadBody.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v7.16.0 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* OpenAPI definition
|
||||||
|
* OpenAPI spec version: v0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type UploadBody = {
|
||||||
|
document: Blob;
|
||||||
|
};
|
||||||
161
app/composeables/api/playlist-controller/playlist-controller.ts
Normal file
161
app/composeables/api/playlist-controller/playlist-controller.ts
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v7.16.0 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* OpenAPI definition
|
||||||
|
* OpenAPI spec version: v0
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
useQuery
|
||||||
|
} from '@tanstack/vue-query';
|
||||||
|
import type {
|
||||||
|
DataTag,
|
||||||
|
MutationFunction,
|
||||||
|
QueryClient,
|
||||||
|
QueryFunction,
|
||||||
|
QueryKey,
|
||||||
|
UseMutationOptions,
|
||||||
|
UseMutationReturnType,
|
||||||
|
UseQueryOptions,
|
||||||
|
UseQueryReturnType
|
||||||
|
} from '@tanstack/vue-query';
|
||||||
|
|
||||||
|
import {
|
||||||
|
unref
|
||||||
|
} from 'vue';
|
||||||
|
import type {
|
||||||
|
MaybeRef
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
PlaylistCreateDTO,
|
||||||
|
PlaylistReadDTO
|
||||||
|
} from '.././models';
|
||||||
|
|
||||||
|
import { axiosInstance } from '.././axios-instance';
|
||||||
|
|
||||||
|
|
||||||
|
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const createPlaylist = (
|
||||||
|
playlistCreateDTO: MaybeRef<PlaylistCreateDTO>,
|
||||||
|
options?: SecondParameter<typeof axiosInstance>,signal?: AbortSignal
|
||||||
|
) => {
|
||||||
|
playlistCreateDTO = unref(playlistCreateDTO);
|
||||||
|
|
||||||
|
return axiosInstance<PlaylistReadDTO>(
|
||||||
|
{url: `/playlist`, method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json', },
|
||||||
|
data: playlistCreateDTO, signal
|
||||||
|
},
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const getCreatePlaylistMutationOptions = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof createPlaylist>>, TError,{data: PlaylistCreateDTO}, TContext>, request?: SecondParameter<typeof axiosInstance>}
|
||||||
|
): UseMutationOptions<Awaited<ReturnType<typeof createPlaylist>>, TError,{data: PlaylistCreateDTO}, TContext> => {
|
||||||
|
|
||||||
|
const mutationKey = ['createPlaylist'];
|
||||||
|
const {mutation: mutationOptions, request: requestOptions} = options ?
|
||||||
|
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
|
||||||
|
options
|
||||||
|
: {...options, mutation: {...options.mutation, mutationKey}}
|
||||||
|
: {mutation: { mutationKey, }, request: undefined};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const mutationFn: MutationFunction<Awaited<ReturnType<typeof createPlaylist>>, {data: PlaylistCreateDTO}> = (props) => {
|
||||||
|
const {data} = props ?? {};
|
||||||
|
|
||||||
|
return createPlaylist(data,requestOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions }}
|
||||||
|
|
||||||
|
export type CreatePlaylistMutationResult = NonNullable<Awaited<ReturnType<typeof createPlaylist>>>
|
||||||
|
export type CreatePlaylistMutationBody = PlaylistCreateDTO
|
||||||
|
export type CreatePlaylistMutationError = unknown
|
||||||
|
|
||||||
|
export const useCreatePlaylist = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof createPlaylist>>, TError,{data: PlaylistCreateDTO}, TContext>, request?: SecondParameter<typeof axiosInstance>}
|
||||||
|
, queryClient?: QueryClient): UseMutationReturnType<
|
||||||
|
Awaited<ReturnType<typeof createPlaylist>>,
|
||||||
|
TError,
|
||||||
|
{data: PlaylistCreateDTO},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
|
||||||
|
const mutationOptions = getCreatePlaylistMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions, queryClient);
|
||||||
|
}
|
||||||
|
export const playlists = (
|
||||||
|
|
||||||
|
options?: SecondParameter<typeof axiosInstance>,signal?: AbortSignal
|
||||||
|
) => {
|
||||||
|
|
||||||
|
|
||||||
|
return axiosInstance<PlaylistReadDTO[]>(
|
||||||
|
{url: `/playlists`, method: 'GET', signal
|
||||||
|
},
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const getPlaylistsQueryKey = () => {
|
||||||
|
return [
|
||||||
|
'playlists'
|
||||||
|
] as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const getPlaylistsQueryOptions = <TData = Awaited<ReturnType<typeof playlists>>, TError = unknown>( options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof playlists>>, TError, TData>>, request?: SecondParameter<typeof axiosInstance>}
|
||||||
|
) => {
|
||||||
|
|
||||||
|
const {query: queryOptions, request: requestOptions} = options ?? {};
|
||||||
|
|
||||||
|
const queryKey = getPlaylistsQueryKey();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const queryFn: QueryFunction<Awaited<ReturnType<typeof playlists>>> = ({ signal }) => playlists(requestOptions, signal);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return { queryKey, queryFn, ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof playlists>>, TError, TData>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PlaylistsQueryResult = NonNullable<Awaited<ReturnType<typeof playlists>>>
|
||||||
|
export type PlaylistsQueryError = unknown
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function usePlaylists<TData = Awaited<ReturnType<typeof playlists>>, TError = unknown>(
|
||||||
|
options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof playlists>>, TError, TData>>, request?: SecondParameter<typeof axiosInstance>}
|
||||||
|
, queryClient?: QueryClient
|
||||||
|
): UseQueryReturnType<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> } {
|
||||||
|
|
||||||
|
const queryOptions = getPlaylistsQueryOptions(options)
|
||||||
|
|
||||||
|
const query = useQuery(queryOptions, queryClient) as UseQueryReturnType<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
|
||||||
|
|
||||||
|
query.queryKey = unref(queryOptions).queryKey as DataTag<QueryKey, TData, TError>;
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
164
app/composeables/api/s-3-controller/s-3-controller.ts
Normal file
164
app/composeables/api/s-3-controller/s-3-controller.ts
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v7.16.0 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* OpenAPI definition
|
||||||
|
* OpenAPI spec version: v0
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
useQuery
|
||||||
|
} from '@tanstack/vue-query';
|
||||||
|
import type {
|
||||||
|
DataTag,
|
||||||
|
MutationFunction,
|
||||||
|
QueryClient,
|
||||||
|
QueryFunction,
|
||||||
|
QueryKey,
|
||||||
|
UseMutationOptions,
|
||||||
|
UseMutationReturnType,
|
||||||
|
UseQueryOptions,
|
||||||
|
UseQueryReturnType
|
||||||
|
} from '@tanstack/vue-query';
|
||||||
|
|
||||||
|
import {
|
||||||
|
unref
|
||||||
|
} from 'vue';
|
||||||
|
import type {
|
||||||
|
MaybeRef
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ReadParams,
|
||||||
|
UploadBody
|
||||||
|
} from '.././models';
|
||||||
|
|
||||||
|
import { axiosInstance } from '.././axios-instance';
|
||||||
|
|
||||||
|
|
||||||
|
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const upload = (
|
||||||
|
uploadBody: MaybeRef<UploadBody>,
|
||||||
|
options?: SecondParameter<typeof axiosInstance>,signal?: AbortSignal
|
||||||
|
) => {
|
||||||
|
uploadBody = unref(uploadBody);
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append(`document`, uploadBody.document)
|
||||||
|
|
||||||
|
return axiosInstance<string>(
|
||||||
|
{url: `/upload`, method: 'POST',
|
||||||
|
headers: {'Content-Type': 'multipart/form-data', },
|
||||||
|
data: formData, signal
|
||||||
|
},
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const getUploadMutationOptions = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof upload>>, TError,{data: UploadBody}, TContext>, request?: SecondParameter<typeof axiosInstance>}
|
||||||
|
): UseMutationOptions<Awaited<ReturnType<typeof upload>>, TError,{data: UploadBody}, TContext> => {
|
||||||
|
|
||||||
|
const mutationKey = ['upload'];
|
||||||
|
const {mutation: mutationOptions, request: requestOptions} = options ?
|
||||||
|
options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ?
|
||||||
|
options
|
||||||
|
: {...options, mutation: {...options.mutation, mutationKey}}
|
||||||
|
: {mutation: { mutationKey, }, request: undefined};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const mutationFn: MutationFunction<Awaited<ReturnType<typeof upload>>, {data: UploadBody}> = (props) => {
|
||||||
|
const {data} = props ?? {};
|
||||||
|
|
||||||
|
return upload(data,requestOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return { mutationFn, ...mutationOptions }}
|
||||||
|
|
||||||
|
export type UploadMutationResult = NonNullable<Awaited<ReturnType<typeof upload>>>
|
||||||
|
export type UploadMutationBody = UploadBody
|
||||||
|
export type UploadMutationError = unknown
|
||||||
|
|
||||||
|
export const useUpload = <TError = unknown,
|
||||||
|
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof upload>>, TError,{data: UploadBody}, TContext>, request?: SecondParameter<typeof axiosInstance>}
|
||||||
|
, queryClient?: QueryClient): UseMutationReturnType<
|
||||||
|
Awaited<ReturnType<typeof upload>>,
|
||||||
|
TError,
|
||||||
|
{data: UploadBody},
|
||||||
|
TContext
|
||||||
|
> => {
|
||||||
|
|
||||||
|
const mutationOptions = getUploadMutationOptions(options);
|
||||||
|
|
||||||
|
return useMutation(mutationOptions, queryClient);
|
||||||
|
}
|
||||||
|
export const read = (
|
||||||
|
params: MaybeRef<ReadParams>,
|
||||||
|
options?: SecondParameter<typeof axiosInstance>,signal?: AbortSignal
|
||||||
|
) => {
|
||||||
|
params = unref(params);
|
||||||
|
|
||||||
|
return axiosInstance<string>(
|
||||||
|
{url: `/read`, method: 'GET',
|
||||||
|
params: unref(params), signal
|
||||||
|
},
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const getReadQueryKey = (params?: MaybeRef<ReadParams>,) => {
|
||||||
|
return [
|
||||||
|
'read', ...(params ? [params]: [])
|
||||||
|
] as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const getReadQueryOptions = <TData = Awaited<ReturnType<typeof read>>, TError = unknown>(params: MaybeRef<ReadParams>, options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof read>>, TError, TData>>, request?: SecondParameter<typeof axiosInstance>}
|
||||||
|
) => {
|
||||||
|
|
||||||
|
const {query: queryOptions, request: requestOptions} = options ?? {};
|
||||||
|
|
||||||
|
const queryKey = getReadQueryKey(params);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const queryFn: QueryFunction<Awaited<ReturnType<typeof read>>> = ({ signal }) => read(params, requestOptions, signal);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return { queryKey, queryFn, ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof read>>, TError, TData>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ReadQueryResult = NonNullable<Awaited<ReturnType<typeof read>>>
|
||||||
|
export type ReadQueryError = unknown
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function useRead<TData = Awaited<ReturnType<typeof read>>, TError = unknown>(
|
||||||
|
params: MaybeRef<ReadParams>, options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof read>>, TError, TData>>, request?: SecondParameter<typeof axiosInstance>}
|
||||||
|
, queryClient?: QueryClient
|
||||||
|
): UseQueryReturnType<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> } {
|
||||||
|
|
||||||
|
const queryOptions = getReadQueryOptions(params,options)
|
||||||
|
|
||||||
|
const query = useQuery(queryOptions, queryClient) as UseQueryReturnType<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
|
||||||
|
|
||||||
|
query.queryKey = unref(queryOptions).queryKey as DataTag<QueryKey, TData, TError>;
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
19
app/plugins/vue-query.ts
Normal file
19
app/plugins/vue-query.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { VueQueryPlugin, QueryClient } from '@tanstack/vue-query'
|
||||||
|
import { defineNuxtPlugin } from 'nuxt/app'
|
||||||
|
|
||||||
|
export const DEFAULT_QUERIES_OPTIONS = {
|
||||||
|
staleTime: 1000 * 60 * 5,
|
||||||
|
retry: 1,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnMount: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: DEFAULT_QUERIES_OPTIONS,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxtApp.vueApp.use(VueQueryPlugin, { queryClient })
|
||||||
|
})
|
||||||
@ -14,5 +14,10 @@ export default defineNuxtConfig({
|
|||||||
plugins: [
|
plugins: [
|
||||||
tailwindcss()
|
tailwindcss()
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
runtimeConfig: {
|
||||||
|
public: {
|
||||||
|
apiBaseUrl: process.env.API_BASE_URL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
34
orval.config.js
Normal file
34
orval.config.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { defineConfig } from 'orval';
|
||||||
|
import * as dotenv from 'dotenv';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
'mp3-composer': {
|
||||||
|
input: {
|
||||||
|
target: 'http://localhost:8080/v3/api-docs',
|
||||||
|
parserOptions: {
|
||||||
|
resolve: {
|
||||||
|
http: {
|
||||||
|
headers: {
|
||||||
|
Authorization:
|
||||||
|
'Basic ' + Buffer.from(`${process.env.API_USERNAME}:${process.env.API_PASSWORD}`).toString('base64'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
target: './app/composeables/api/composer.ts',
|
||||||
|
schemas: './app/composeables/api/models',
|
||||||
|
mode: 'tags-split',
|
||||||
|
client: 'vue-query',
|
||||||
|
override: {
|
||||||
|
mutator: {
|
||||||
|
path: './app/composeables/api/axios-instance.ts',
|
||||||
|
name: 'axiosInstance',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -14,9 +14,12 @@
|
|||||||
"@nuxt/icon": "2.1.0",
|
"@nuxt/icon": "2.1.0",
|
||||||
"@nuxt/image": "1.11.0",
|
"@nuxt/image": "1.11.0",
|
||||||
"@tailwindcss/vite": "^4.1.16",
|
"@tailwindcss/vite": "^4.1.16",
|
||||||
|
"@tanstack/vue-query": "^5.90.7",
|
||||||
"@vueuse/core": "^14.0.0",
|
"@vueuse/core": "^14.0.0",
|
||||||
|
"axios": "^1.13.2",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
"lucide-vue-next": "^0.548.0",
|
"lucide-vue-next": "^0.548.0",
|
||||||
"nuxt": "^4.2.0",
|
"nuxt": "^4.2.0",
|
||||||
"oidc-client-ts": "^3.3.0",
|
"oidc-client-ts": "^3.3.0",
|
||||||
@ -29,6 +32,7 @@
|
|||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"orval": "^7.16.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user