parent
441f45e1c7
commit
4c2c66ecdd
@ -0,0 +1,110 @@
|
|||||||
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { trpc } from '~/utils/trpc';
|
||||||
|
|
||||||
|
import type { ExpandedTypeaheadProps } from './ExpandedTypeahead';
|
||||||
|
import ExpandedTypeahead from './ExpandedTypeahead';
|
||||||
|
|
||||||
|
export type TagTypeaheadProps = Omit<
|
||||||
|
ExpandedTypeaheadProps,
|
||||||
|
'clearOnSelect' | 'filterOption' | 'label' | 'onQueryChange' | 'options'
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const CREATE_ID = 'create';
|
||||||
|
|
||||||
|
export default function TagTypeahead({
|
||||||
|
onSelect,
|
||||||
|
...restProps
|
||||||
|
}: TagTypeaheadProps) {
|
||||||
|
const [query, setQuery] = useState('');
|
||||||
|
|
||||||
|
const utils = trpc.useContext();
|
||||||
|
const { data: tags } = trpc.useQuery(
|
||||||
|
[
|
||||||
|
'questions.tags.getTags',
|
||||||
|
{
|
||||||
|
name: query,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
keepPreviousData: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const { mutateAsync: createTagAsync } = trpc.useMutation(
|
||||||
|
'questions.tags.user.create',
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
utils.invalidateQueries(['questions.tags.getTags']);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const tagOptions = useMemo(() => {
|
||||||
|
return (
|
||||||
|
tags?.map(({ id, tag }) => ({
|
||||||
|
id,
|
||||||
|
label: tag,
|
||||||
|
value: id,
|
||||||
|
})) ?? []
|
||||||
|
);
|
||||||
|
}, [tags]);
|
||||||
|
|
||||||
|
const filteredOptions = useMemo(() => {
|
||||||
|
const options = tagOptions.filter(
|
||||||
|
({ id, label }) =>
|
||||||
|
id === CREATE_ID || label.toLowerCase().includes(query.toLowerCase()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (query === '' || tags?.find(({ tag }) => tag === query)) {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
...options,
|
||||||
|
{
|
||||||
|
id: CREATE_ID,
|
||||||
|
label: `Create "${query}"`,
|
||||||
|
value: query,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, [query, tagOptions, tags]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ExpandedTypeahead
|
||||||
|
onSelect={async (option) => {
|
||||||
|
if (option.id === CREATE_ID) {
|
||||||
|
const { value } = option;
|
||||||
|
|
||||||
|
setQuery('');
|
||||||
|
onSelect({
|
||||||
|
id: 'dummy',
|
||||||
|
label: value,
|
||||||
|
value: 'dummy',
|
||||||
|
});
|
||||||
|
await createTagAsync(
|
||||||
|
{ tag: value },
|
||||||
|
{
|
||||||
|
onSuccess: async (tag) => {
|
||||||
|
onSelect({
|
||||||
|
id: tag.id,
|
||||||
|
label: tag.tag,
|
||||||
|
value: tag.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
onSelect(option);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{...(restProps as Omit<
|
||||||
|
ExpandedTypeaheadProps & { clearOnSelect: boolean },
|
||||||
|
'clearOnSelect' | 'onSelect'
|
||||||
|
>)}
|
||||||
|
label="Tag"
|
||||||
|
options={filteredOptions}
|
||||||
|
onQueryChange={setQuery}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { createRouter } from '../context';
|
||||||
|
|
||||||
|
export const questionsQuestionTagRouter = createRouter().query('getTags', {
|
||||||
|
input: z.object({}),
|
||||||
|
async resolve({ ctx }) {
|
||||||
|
return await ctx.prisma.questionsQuestionTag.findMany({});
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in new issue