Compare commits
5 Commits
672677396f
...
37c6b318a6
| Author | SHA1 | Date | |
|---|---|---|---|
| 37c6b318a6 | |||
| 0f16f9d209 | |||
| ed59327cb7 | |||
| 4c0e463aec | |||
| 8018d21011 |
@ -112,7 +112,24 @@ export default [
|
||||
"@typescript-eslint/no-misused-promises": ["error", {
|
||||
"checksVoidReturn": false
|
||||
}
|
||||
]
|
||||
],
|
||||
'@typescript-eslint/naming-convention': ['error',
|
||||
{
|
||||
selector: 'enum',
|
||||
format: ['UPPER_CASE', 'PascalCase'],
|
||||
},
|
||||
{
|
||||
selector: 'variable',
|
||||
format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
|
||||
},
|
||||
{
|
||||
selector: 'function',
|
||||
format: ['camelCase', 'PascalCase'],
|
||||
},
|
||||
{
|
||||
selector: 'typeLike',
|
||||
format: ['PascalCase'],
|
||||
}],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
"@radix-ui/react-switch": "1.2.5",
|
||||
"@radix-ui/react-tabs": "^1.1.12",
|
||||
"@radix-ui/react-tooltip": "1.2.7",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"apollo-upload-client": "18.0.1",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"clsx": "2.1.1",
|
||||
@ -55,6 +56,7 @@
|
||||
"@next/eslint-plugin-next": "15.4.5",
|
||||
"@parcel/watcher": "^2.5.1",
|
||||
"@tailwindcss/postcss": "4.1.11",
|
||||
"@types/apollo-upload-client": "18.0.0",
|
||||
"@types/node": "22.17.0",
|
||||
"@types/react": "19.1.9",
|
||||
"@types/react-dom": "19.1.7",
|
||||
|
||||
20
frontend/src/app/(site)/dashboard/chat/page.tsx
Normal file
20
frontend/src/app/(site)/dashboard/chat/page.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
import { ChangeChatSettings } from '@/components/features/chat/settings/ChangeChatSettings';
|
||||
import { NO_INDEX_PAGE } from '@/libs/constants/seo.constants';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const t = await getTranslations('dashboard.chat.header');
|
||||
|
||||
return {
|
||||
title: t('heading'),
|
||||
description: t('description'),
|
||||
...NO_INDEX_PAGE,
|
||||
};
|
||||
}
|
||||
|
||||
const ChatSettingsPage = () => <ChangeChatSettings />;
|
||||
|
||||
export default ChatSettingsPage;
|
||||
20
frontend/src/app/(site)/dashboard/followers/page.tsx
Normal file
20
frontend/src/app/(site)/dashboard/followers/page.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
import { FollowersTable } from '@/components/features/follow/table/FollowersTable';
|
||||
import { NO_INDEX_PAGE } from '@/libs/constants/seo.constants';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const t = await getTranslations('dashboard.followers.header');
|
||||
|
||||
return {
|
||||
title: t('heading'),
|
||||
description: t('description'),
|
||||
...NO_INDEX_PAGE,
|
||||
};
|
||||
}
|
||||
|
||||
const FollowersPage = () => <FollowersTable />;
|
||||
|
||||
export default FollowersPage;
|
||||
20
frontend/src/app/(site)/dashboard/keys/page.tsx
Normal file
20
frontend/src/app/(site)/dashboard/keys/page.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
import { KeysSettings } from '@/components/features/keys/settings/KeysSettings';
|
||||
import { NO_INDEX_PAGE } from '@/libs/constants/seo.constants';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const t = await getTranslations('dashboard.keys.header');
|
||||
|
||||
return {
|
||||
title: t('heading'),
|
||||
description: t('description'),
|
||||
...NO_INDEX_PAGE,
|
||||
};
|
||||
}
|
||||
|
||||
const KeysSettingsPage = () => <KeysSettings />;
|
||||
|
||||
export default KeysSettingsPage;
|
||||
20
frontend/src/app/(site)/dashboard/plans/page.tsx
Normal file
20
frontend/src/app/(site)/dashboard/plans/page.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
import { PlansTable } from '@/components/features/sponsorship/plan/table/PlansTable';
|
||||
import { NO_INDEX_PAGE } from '@/libs/constants/seo.constants';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const t = await getTranslations('dashboard.plans.header');
|
||||
|
||||
return {
|
||||
title: t('heading'),
|
||||
description: t('description'),
|
||||
...NO_INDEX_PAGE,
|
||||
};
|
||||
}
|
||||
|
||||
const PlansPage = () => <PlansTable />;
|
||||
|
||||
export default PlansPage;
|
||||
20
frontend/src/app/(site)/dashboard/sponsors/page.tsx
Normal file
20
frontend/src/app/(site)/dashboard/sponsors/page.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
import { SponsorsTable } from '@/components/features/sponsorship/subscription/table/SponsorsTable';
|
||||
import { NO_INDEX_PAGE } from '@/libs/constants/seo.constants';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const t = await getTranslations('dashboard.sponsors.header');
|
||||
|
||||
return {
|
||||
title: t('heading'),
|
||||
description: t('description'),
|
||||
...NO_INDEX_PAGE,
|
||||
};
|
||||
}
|
||||
|
||||
const SponsorsPage = () => <SponsorsTable />;
|
||||
|
||||
export default SponsorsPage;
|
||||
20
frontend/src/app/(site)/dashboard/transactions/page.tsx
Normal file
20
frontend/src/app/(site)/dashboard/transactions/page.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
import { TransactionsTable } from '@/components/features/sponsorship/transactions/table/TransactionsTable';
|
||||
import { NO_INDEX_PAGE } from '@/libs/constants/seo.constants';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const t = await getTranslations('dashboard.transactions.header');
|
||||
|
||||
return {
|
||||
title: t('heading'),
|
||||
description: t('description'),
|
||||
...NO_INDEX_PAGE,
|
||||
};
|
||||
}
|
||||
|
||||
const TransactionsPage = () => <TransactionsTable />;
|
||||
|
||||
export default TransactionsPage;
|
||||
@ -14,6 +14,5 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
};
|
||||
}
|
||||
|
||||
export default function DeactivatePage() {
|
||||
return <DeactivateForm />;
|
||||
}
|
||||
const DeactivatePage = () => <DeactivateForm />;
|
||||
export default DeactivatePage;
|
||||
|
||||
@ -63,7 +63,7 @@ export const DeactivateForm = () => {
|
||||
const { isValid } = form.formState;
|
||||
|
||||
function onSubmit(data: TypeDeactivateSchema) {
|
||||
deactivate({ variables: { data } });
|
||||
void deactivate({ variables: { data } });
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Form, FormField } from '@/components/ui/common/Form';
|
||||
import { Heading } from '@/components/ui/elements/Heading';
|
||||
import {
|
||||
ToggleCard,
|
||||
ToggleCardSkeleton,
|
||||
} from '@/components/ui/elements/ToggleCard';
|
||||
import { useChangeChatSettingsMutation } from '@/graphql/generated/output';
|
||||
import { useCurrent } from '@/hooks/useCurrent';
|
||||
import { changeChatSettingsSchema } from '@/schemas/chat/change-chat.settings.schema';
|
||||
|
||||
import type { TypeChangeChatSettingsSchema } from '@/schemas/chat/change-chat.settings.schema';
|
||||
|
||||
export const ChangeChatSettings = () => {
|
||||
const t = useTranslations('dashboard.chat');
|
||||
|
||||
const { user, isLoadingProfile } = useCurrent();
|
||||
|
||||
const form = useForm<TypeChangeChatSettingsSchema>({
|
||||
resolver: zodResolver(changeChatSettingsSchema),
|
||||
values: {
|
||||
isChatEnable: user?.stream.isChatEnable ?? false,
|
||||
isChatFollowersOnly: user?.stream.isChatFollowersOnly ?? false,
|
||||
isChatPremiumFollowersOnly:
|
||||
user?.stream.isChatPremiumFollowersOnly ?? false,
|
||||
},
|
||||
});
|
||||
|
||||
const [update, { loading: isLoadingUpdate }] = useChangeChatSettingsMutation({
|
||||
onCompleted(data) {
|
||||
toast.success(t('successMessage'));
|
||||
},
|
||||
onError() {
|
||||
toast.error(t('errorMessage'));
|
||||
},
|
||||
});
|
||||
|
||||
function onChange(
|
||||
field: keyof TypeChangeChatSettingsSchema,
|
||||
value: boolean,
|
||||
) {
|
||||
form.setValue(field, value);
|
||||
|
||||
void update({
|
||||
variables: {
|
||||
data: { ...form.getValues(), [field]: value },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="lg:px-10">
|
||||
<Heading
|
||||
description={t('header.description')}
|
||||
size="lg"
|
||||
title={t('header.heading')}
|
||||
/>
|
||||
|
||||
<div className="mt-3 space-y-6">
|
||||
{isLoadingProfile
|
||||
? Array.from({ length: 3 }).map((_, index) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<ToggleCardSkeleton key={index} />
|
||||
))
|
||||
: (
|
||||
<Form {...form}>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="isChatEnable"
|
||||
render={({ field }) => (
|
||||
<ToggleCard
|
||||
description={t('isChatEnabled.description')}
|
||||
heading={t('isChatEnabled.heading')}
|
||||
isDisabled={isLoadingUpdate}
|
||||
value={field.value}
|
||||
onChange={(value) => { onChange('isChatEnable', value); }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="isChatFollowersOnly"
|
||||
render={({ field }) => (
|
||||
<ToggleCard
|
||||
description={t(
|
||||
'isChatFollowersOnly.description',
|
||||
)}
|
||||
heading={t('isChatFollowersOnly.heading')}
|
||||
isDisabled={isLoadingUpdate}
|
||||
value={field.value}
|
||||
onChange={(value) => { onChange('isChatFollowersOnly', value); }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="isChatPremiumFollowersOnly"
|
||||
render={({ field }) => (
|
||||
<ToggleCard
|
||||
description={t(
|
||||
'isChatPremiumFollowersOnly.description',
|
||||
)}
|
||||
heading={t(
|
||||
'isChatPremiumFollowersOnly.heading',
|
||||
)}
|
||||
isDisabled={
|
||||
isLoadingUpdate || !user?.isVerified
|
||||
}
|
||||
value={field.value}
|
||||
onChange={(value) => {
|
||||
onChange(
|
||||
'isChatPremiumFollowersOnly',
|
||||
value,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
106
frontend/src/components/features/follow/table/FollowersTable.tsx
Normal file
106
frontend/src/components/features/follow/table/FollowersTable.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
'use client';
|
||||
|
||||
import { MoreHorizontal, User } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { Button } from '@/components/ui/common/Button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/common/DropdownMenu';
|
||||
import { ChannelAvatar } from '@/components/ui/elements/ChannelAvatar';
|
||||
import { ChannelVerified } from '@/components/ui/elements/ChannelVerified';
|
||||
import { DataTable, DataTableSkeleton } from '@/components/ui/elements/DataTable';
|
||||
import { Heading } from '@/components/ui/elements/Heading';
|
||||
import {
|
||||
type FindMyFollowersQuery,
|
||||
useFindMyFollowersQuery,
|
||||
} from '@/graphql/generated/output';
|
||||
import { formatDate } from '@/utils/format-date';
|
||||
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
const FollowerCell = ({ row }: any) => (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<ChannelAvatar channel={row.original.follower} size="sm" />
|
||||
|
||||
<h2>
|
||||
{row.original.follower.name}
|
||||
</h2>
|
||||
|
||||
{row.original.follower.isVerified ? <ChannelVerified size="sm" /> : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
const FollowerActionCell = ({ row }: any) => {
|
||||
const t = useTranslations('dashboard.followers');
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button className="size-8 p-0" variant="ghost">
|
||||
<MoreHorizontal className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent side="right">
|
||||
<Link
|
||||
href={`/${row.original.follower.name}`}
|
||||
target="_blank"
|
||||
>
|
||||
<DropdownMenuItem>
|
||||
<User className="mr-2 size-4" />
|
||||
|
||||
{t('columns.viewChannel')}
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export const FollowersTable = () => {
|
||||
const t = useTranslations('dashboard.followers');
|
||||
|
||||
const { data, loading: isLoadingFollowers } = useFindMyFollowersQuery();
|
||||
const followers = data?.findMyFollowers ?? [];
|
||||
|
||||
const followersColumns: ColumnDef<FindMyFollowersQuery['findMyFollowers'][0]>[] = [
|
||||
{
|
||||
accessorKey: 'createdAt',
|
||||
header: t('columns.date'),
|
||||
cell: ({ row }) => formatDate(row.original.createdAt),
|
||||
},
|
||||
{
|
||||
accessorKey: 'follower',
|
||||
header: t('columns.user'),
|
||||
cell: FollowerCell,
|
||||
},
|
||||
{
|
||||
accessorKey: 'actions',
|
||||
header: t('columns.actions'),
|
||||
cell: FollowerActionCell,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="lg:px-10">
|
||||
<Heading
|
||||
description={t('header.description')}
|
||||
size="lg"
|
||||
title={t('header.heading')}
|
||||
/>
|
||||
|
||||
<div className="mt-5">
|
||||
{
|
||||
isLoadingFollowers
|
||||
? <DataTableSkeleton />
|
||||
: <DataTable columns={followersColumns} data={followers} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,173 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { Button } from '@/components/ui/common/Button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/common/Dialog';
|
||||
|
||||
export const InstructionModal = () => {
|
||||
const t = useTranslations('dashboard.keys.instructionModal');
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="secondary">
|
||||
{t('trigger')}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent className="max-h-[80vh] max-w-[800px] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-xl">
|
||||
{t('heading')}
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription className="text-sm text-muted-foreground">
|
||||
{t('description')}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('step1Title')}
|
||||
</h2>
|
||||
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('step1Description')}
|
||||
</p>
|
||||
|
||||
<ol className="list-inside list-decimal space-y-2 pl-4">
|
||||
<li className="text-sm text-muted-foreground">
|
||||
<strong>
|
||||
{t('downloadObs')}
|
||||
</strong>
|
||||
|
||||
<br />
|
||||
|
||||
{t('downloadObsDescription')}
|
||||
|
||||
{' '}
|
||||
|
||||
<a
|
||||
className="text-blue-500 underline hover:text-blue-700"
|
||||
href="https://obsproject.com"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
{t('obsLinkText')}
|
||||
</a>
|
||||
.
|
||||
</li>
|
||||
|
||||
<li className="text-sm text-muted-foreground">
|
||||
<strong>
|
||||
{t('copyKeys')}
|
||||
</strong>
|
||||
|
||||
<br />
|
||||
|
||||
{t('copyKeysDescription')}
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h2 className="mt-4 text-lg font-semibold">
|
||||
{t('step2Title')}
|
||||
</h2>
|
||||
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('step2Description')}
|
||||
</p>
|
||||
|
||||
<ol className="list-inside list-decimal space-y-2 pl-4">
|
||||
<li className="text-sm text-muted-foreground">
|
||||
<strong>
|
||||
{t('openObs')}
|
||||
</strong>
|
||||
|
||||
<br />
|
||||
|
||||
{t('openObsDescription')}
|
||||
</li>
|
||||
|
||||
<li className="text-sm text-muted-foreground">
|
||||
<strong>
|
||||
{t('openStreamSettings')}
|
||||
</strong>
|
||||
|
||||
<br />
|
||||
|
||||
{t('openStreamSettingsDescription')}
|
||||
</li>
|
||||
|
||||
<li className="text-sm text-muted-foreground">
|
||||
<strong>
|
||||
{t('enterDetails')}
|
||||
</strong>
|
||||
|
||||
<br />
|
||||
|
||||
{t('enterDetailsDescription')}
|
||||
</li>
|
||||
|
||||
<li className="text-sm text-muted-foreground">
|
||||
<strong>
|
||||
{t('saveSettings')}
|
||||
</strong>
|
||||
|
||||
<br />
|
||||
|
||||
{t('saveSettingsDescription')}
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h2 className="mt-4 text-lg font-semibold">
|
||||
{t('step3Title')}
|
||||
</h2>
|
||||
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('step3Description')}
|
||||
</p>
|
||||
|
||||
<ol className="list-inside list-decimal space-y-2 pl-4">
|
||||
<li className="text-sm text-muted-foreground">
|
||||
<strong>
|
||||
{t('startStream')}
|
||||
</strong>
|
||||
|
||||
<br />
|
||||
|
||||
{t('startStreamDescription')}
|
||||
</li>
|
||||
|
||||
<li className="text-sm text-muted-foreground">
|
||||
<strong>
|
||||
{t('monitorStream')}
|
||||
</strong>
|
||||
|
||||
<br />
|
||||
|
||||
{t('monitorStreamDescription')}
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p className="mt-4 text-sm text-muted-foreground">
|
||||
{t('congrats')}
|
||||
</p>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button variant="secondary">
|
||||
{t('close')}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,51 @@
|
||||
'use client';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { Heading } from '@/components/ui/elements/Heading';
|
||||
import { ToggleCardSkeleton } from '@/components/ui/elements/ToggleCard';
|
||||
import { useCurrent } from '@/hooks/useCurrent';
|
||||
|
||||
import { CreateIngressForm } from './forms/CreateIngressForm';
|
||||
import { StreamKey } from './forms/StreamKey';
|
||||
import { StreamURL } from './forms/StreamURL';
|
||||
import { InstructionModal } from './InstructionModal';
|
||||
|
||||
export const KeysSettings = () => {
|
||||
const t = useTranslations('dashboard.keys.header');
|
||||
|
||||
const { user, isLoadingProfile } = useCurrent();
|
||||
|
||||
return (
|
||||
<div className="lg:px-10">
|
||||
<div className="block items-center justify-between space-y-3 lg:flex lg:space-y-0">
|
||||
<Heading
|
||||
description={t('description')}
|
||||
size="lg"
|
||||
title={t('heading')}
|
||||
/>
|
||||
|
||||
<div className="flex items-center gap-x-4">
|
||||
<InstructionModal />
|
||||
|
||||
<CreateIngressForm />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 space-y-6">
|
||||
{isLoadingProfile
|
||||
? Array.from({ length: 2 }).map((_, index) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<ToggleCardSkeleton key={index} />
|
||||
))
|
||||
: (
|
||||
<>
|
||||
<StreamURL value={user?.stream?.serverUrl} />
|
||||
|
||||
<StreamKey value={user?.stream?.key} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,149 @@
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Button } from '@/components/ui/common/Button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/common/Dialog';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
} from '@/components/ui/common/Form';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/common/Select';
|
||||
import { useCreateIngressMutation } from '@/graphql/generated/output';
|
||||
import { useCurrent } from '@/hooks/useCurrent';
|
||||
import {
|
||||
IngressType,
|
||||
type TypeCreateIngressSchema,
|
||||
createIngressSchema,
|
||||
} from '@/schemas/stream/create-ingress.schema';
|
||||
|
||||
export const CreateIngressForm = () => {
|
||||
const t = useTranslations('dashboard.keys.createModal');
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { refetch } = useCurrent();
|
||||
|
||||
const form = useForm<TypeCreateIngressSchema>({
|
||||
resolver: zodResolver(createIngressSchema),
|
||||
defaultValues: {
|
||||
ingressType: IngressType.RTMP,
|
||||
},
|
||||
});
|
||||
|
||||
const [create, { loading: isLoadingCreate }] = useCreateIngressMutation({
|
||||
onCompleted() {
|
||||
setIsOpen(false);
|
||||
void refetch();
|
||||
toast.success(t('successMessage'));
|
||||
},
|
||||
onError() {
|
||||
toast.error(t('errorMessage'));
|
||||
},
|
||||
});
|
||||
|
||||
const { isValid } = form.formState;
|
||||
|
||||
function onSubmit(data: TypeCreateIngressSchema) {
|
||||
void create({ variables: { ingressType: data.ingressType } });
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>
|
||||
{t('trigger')}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t('heading')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="space-y-6"
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="ingressType"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t('ingressTypeLabel')}
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<Select
|
||||
defaultValue={field.value.toString()}
|
||||
onValueChange={(value) => {
|
||||
field.onChange(Number(value));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
'ingressTypePlaceholder',
|
||||
)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
|
||||
<SelectContent>
|
||||
<SelectItem
|
||||
disabled={isLoadingCreate}
|
||||
value={IngressType.RTMP.toString()}
|
||||
>
|
||||
RTMP
|
||||
</SelectItem>
|
||||
|
||||
<SelectItem
|
||||
disabled={isLoadingCreate}
|
||||
value={IngressType.WHIP.toString()}
|
||||
>
|
||||
WHIP
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
{t('ingressTypeDescription')}
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button disabled={!isValid || isLoadingCreate}>
|
||||
{t('submitButton')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,47 @@
|
||||
import { Eye, EyeOff } from 'lucide-react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Button } from '@/components/ui/common/Button';
|
||||
import { Input } from '@/components/ui/common/Input';
|
||||
import { CardContainer } from '@/components/ui/elements/CardContainer';
|
||||
import { CopyButton } from '@/components/ui/elements/CopyButton';
|
||||
|
||||
type StreamKeyProps = {
|
||||
value?: string | null;
|
||||
};
|
||||
|
||||
export const StreamKey = ({ value }: StreamKeyProps) => {
|
||||
const t = useTranslations('dashboard.keys.key');
|
||||
|
||||
const [isShow, setIsShow] = useState(false);
|
||||
|
||||
const Icon = isShow ? Eye : EyeOff;
|
||||
|
||||
return (
|
||||
<CardContainer
|
||||
isRightContentFull
|
||||
heading={t('heading')}
|
||||
rightContent={(
|
||||
<div className="flex w-full items-center gap-x-4">
|
||||
<Input
|
||||
disabled
|
||||
placeholder={t('heading')}
|
||||
type={isShow ? 'text' : 'password'}
|
||||
value={value ?? ''}
|
||||
/>
|
||||
|
||||
<CopyButton value={value} />
|
||||
|
||||
<Button
|
||||
size="lgIcon"
|
||||
variant="ghost"
|
||||
onClick={() => { setIsShow(!isShow); }}
|
||||
>
|
||||
<Icon className="size-5" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { Input } from '@/components/ui/common/Input';
|
||||
import { CardContainer } from '@/components/ui/elements/CardContainer';
|
||||
import { CopyButton } from '@/components/ui/elements/CopyButton';
|
||||
|
||||
type StreamURLProps = {
|
||||
value?: string | null;
|
||||
};
|
||||
|
||||
export const StreamURL = ({ value }: StreamURLProps) => {
|
||||
const t = useTranslations('dashboard.keys.url');
|
||||
|
||||
return (
|
||||
<CardContainer
|
||||
isRightContentFull
|
||||
heading={t('heading')}
|
||||
rightContent={(
|
||||
<div className="flex w-full items-center gap-x-4">
|
||||
<Input
|
||||
disabled
|
||||
placeholder={t('heading')}
|
||||
value={value ?? ''}
|
||||
/>
|
||||
|
||||
<CopyButton value={value} />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,174 @@
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Button } from '@/components/ui/common/Button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/common/Dialog';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
} from '@/components/ui/common/Form';
|
||||
import { Input } from '@/components/ui/common/Input';
|
||||
import { Textarea } from '@/components/ui/common/Textarea';
|
||||
import {
|
||||
useCreateSponsorshipPlanMutation,
|
||||
useFindMySponsorshipPlansQuery,
|
||||
} from '@/graphql/generated/output';
|
||||
import {
|
||||
type TypeCreatePlanSchema,
|
||||
createPlanSchema,
|
||||
} from '@/schemas/plan/create-plan.schema';
|
||||
|
||||
export const CreatePlanForm = () => {
|
||||
const t = useTranslations('dashboard.plans.createForm');
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { refetch } = useFindMySponsorshipPlansQuery();
|
||||
|
||||
const form = useForm<TypeCreatePlanSchema>({
|
||||
resolver: zodResolver(createPlanSchema),
|
||||
defaultValues: {
|
||||
title: '',
|
||||
description: '',
|
||||
price: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const [create, { loading: isLoadingCreate }] = useCreateSponsorshipPlanMutation({
|
||||
onCompleted() {
|
||||
setIsOpen(false);
|
||||
form.reset();
|
||||
void refetch();
|
||||
toast.success(t('successMessage'));
|
||||
},
|
||||
onError() {
|
||||
toast.error(t('errorMessage'));
|
||||
},
|
||||
});
|
||||
|
||||
const { isValid } = form.formState;
|
||||
|
||||
function onSubmit(data: TypeCreatePlanSchema) {
|
||||
void create({ variables: { data } });
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>
|
||||
{t('trigger')}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t('heading')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="space-y-6"
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t('titleLabel')}
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<Input
|
||||
disabled={isLoadingCreate}
|
||||
placeholder={t('titlePlaceholder')}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
{t('titleDescription')}
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="description"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t('descriptionLabel')}
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<Textarea
|
||||
disabled={isLoadingCreate}
|
||||
placeholder={t(
|
||||
'descriptionPlaceholder',
|
||||
)}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
{t('descriptionDescription')}
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="price"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t('priceLabel')}
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<Input
|
||||
disabled={isLoadingCreate}
|
||||
placeholder={t('priceLabel')}
|
||||
type="number"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
{t('priceDescription')}
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button disabled={!isValid || isLoadingCreate}>
|
||||
{t('submitButton')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,135 @@
|
||||
'use client';
|
||||
|
||||
import { MoreHorizontal, Trash } from 'lucide-react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Button } from '@/components/ui/common/Button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/common/DropdownMenu';
|
||||
import {
|
||||
DataTable,
|
||||
DataTableSkeleton,
|
||||
} from '@/components/ui/elements/DataTable';
|
||||
import { Heading } from '@/components/ui/elements/Heading';
|
||||
import {
|
||||
type FindMySponsorshipPlansQuery,
|
||||
useFindMySponsorshipPlansQuery,
|
||||
useRemoveSponsorshipPlanMutation,
|
||||
} from '@/graphql/generated/output';
|
||||
import { useCurrent } from '@/hooks/useCurrent';
|
||||
import { convertPrice } from '@/utils/convert-price';
|
||||
import { formatDate } from '@/utils/format-date';
|
||||
|
||||
import { CreatePlanForm } from '../forms/CreatePlanForm';
|
||||
|
||||
import { VerifiedChannelAlert } from './VerifiedChannelAlert';
|
||||
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
export const PlansTable = () => {
|
||||
const t = useTranslations('dashboard.plans');
|
||||
|
||||
const { user } = useCurrent();
|
||||
|
||||
const {
|
||||
data,
|
||||
loading: isLoadingPlans,
|
||||
refetch,
|
||||
} = useFindMySponsorshipPlansQuery();
|
||||
const plans = data?.findMySponsorshipPlans ?? [];
|
||||
|
||||
const plansColumns: ColumnDef<
|
||||
FindMySponsorshipPlansQuery['findMySponsorshipPlans'][0]
|
||||
>[] = [
|
||||
{
|
||||
accessorKey: 'createdAt',
|
||||
header: t('columns.date'),
|
||||
cell: ({ row }) => formatDate(row.original.createdAt),
|
||||
},
|
||||
{
|
||||
accessorKey: 'title',
|
||||
header: t('columns.title'),
|
||||
cell: ({ row }) => row.original.title,
|
||||
},
|
||||
{
|
||||
accessorKey: 'price',
|
||||
header: t('columns.price'),
|
||||
cell: ({ row }) => convertPrice(row.original.price),
|
||||
},
|
||||
{
|
||||
accessorKey: 'actions',
|
||||
header: t('columns.actions'),
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
cell: ({ row }) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [remove, { loading: isLoadingRemove }] = useRemoveSponsorshipPlanMutation({
|
||||
onCompleted() {
|
||||
void refetch();
|
||||
toast.success(t('columns.successMessage'));
|
||||
},
|
||||
onError() {
|
||||
// @ts-ignore
|
||||
toast.error(t('columns.errorMessage'));
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button className="size-8 p-0" variant="ghost">
|
||||
<MoreHorizontal className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent side="right">
|
||||
<DropdownMenuItem
|
||||
className="text-red-500 focus:text-red-500"
|
||||
disabled={isLoadingRemove}
|
||||
onClick={async () => remove({
|
||||
variables: { planId: row.original.id },
|
||||
})}
|
||||
>
|
||||
<Trash className="mr-2 size-4" />
|
||||
|
||||
{t('columns.remove')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return user?.isVerified
|
||||
? (
|
||||
<div className="lg:px-10">
|
||||
<div className="block items-center justify-between space-y-3 lg:flex lg:space-y-0">
|
||||
<Heading
|
||||
description={t('header.description')}
|
||||
size="lg"
|
||||
title={t('header.heading')}
|
||||
/>
|
||||
|
||||
<CreatePlanForm />
|
||||
</div>
|
||||
|
||||
<div className="mt-5">
|
||||
{isLoadingPlans
|
||||
? (
|
||||
<DataTableSkeleton />
|
||||
)
|
||||
: (
|
||||
<DataTable columns={plansColumns} data={plans} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<VerifiedChannelAlert />
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,20 @@
|
||||
import { ShieldAlert } from 'lucide-react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
export const VerifiedChannelAlert = () => {
|
||||
const t = useTranslations('dashboard.plans.alert');
|
||||
|
||||
return (
|
||||
<div className="flex h-[75vh] w-full flex-col items-center justify-center">
|
||||
<ShieldAlert className="size-20 text-muted-foreground" />
|
||||
|
||||
<h1 className="mt-6 text-2xl font-semibold">
|
||||
{t('heading')}
|
||||
</h1>
|
||||
|
||||
<p className="mt-3 w-full items-center text-center text-muted-foreground lg:w-[60%]">
|
||||
{t('description')}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,112 @@
|
||||
'use client';
|
||||
|
||||
import { MoreHorizontal, User } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { Button } from '@/components/ui/common/Button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/common/DropdownMenu';
|
||||
import { ChannelAvatar } from '@/components/ui/elements/ChannelAvatar';
|
||||
import { ChannelVerified } from '@/components/ui/elements/ChannelVerified';
|
||||
import {
|
||||
DataTable,
|
||||
DataTableSkeleton,
|
||||
} from '@/components/ui/elements/DataTable';
|
||||
import { Heading } from '@/components/ui/elements/Heading';
|
||||
import {
|
||||
type FindMySponsorsQuery,
|
||||
useFindMySponsorsQuery,
|
||||
} from '@/graphql/generated/output';
|
||||
import { formatDate } from '@/utils/format-date';
|
||||
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
export const SponsorsTable = () => {
|
||||
const t = useTranslations('dashboard.sponsors');
|
||||
|
||||
const { data, loading: isLoadingSponsors } = useFindMySponsorsQuery();
|
||||
const sponsors = data?.findMySponsors ?? [];
|
||||
|
||||
const sponsorsColumns: ColumnDef<
|
||||
FindMySponsorsQuery['findMySponsors'][0]
|
||||
>[] = [
|
||||
{
|
||||
accessorKey: 'expiresAt',
|
||||
header: t('columns.date'),
|
||||
cell: ({ row }) => formatDate(row.original.expiresAt),
|
||||
},
|
||||
{
|
||||
accessorKey: 'user',
|
||||
header: t('columns.user'),
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<ChannelAvatar channel={row.original.user} size="sm" />
|
||||
|
||||
<h2>
|
||||
{row.original.user.name}
|
||||
</h2>
|
||||
|
||||
{row.original.user.isVerified ? <ChannelVerified size="sm" /> : null}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'plan',
|
||||
header: t('columns.plan'),
|
||||
cell: ({ row }) => row.original.plan.title,
|
||||
},
|
||||
{
|
||||
accessorKey: 'actions',
|
||||
header: t('columns.actions'),
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
cell: ({ row }) => (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button className="size-8 p-0" variant="ghost">
|
||||
<MoreHorizontal className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent side="right">
|
||||
<Link
|
||||
href={`/${row.original.user.name}`}
|
||||
target="_blank"
|
||||
>
|
||||
<DropdownMenuItem>
|
||||
<User className="mr-2 size-4" />
|
||||
|
||||
{t('columns.viewChannel')}
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="lg:px-10">
|
||||
<Heading
|
||||
description={t('header.description')}
|
||||
size="lg"
|
||||
title={t('header.heading')}
|
||||
/>
|
||||
|
||||
<div className="mt-5">
|
||||
{isLoadingSponsors
|
||||
? (
|
||||
<DataTableSkeleton />
|
||||
)
|
||||
: (
|
||||
<DataTable columns={sponsorsColumns} data={sponsors} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,115 @@
|
||||
'use client';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import {
|
||||
DataTable,
|
||||
DataTableSkeleton,
|
||||
} from '@/components/ui/elements/DataTable';
|
||||
import { Heading } from '@/components/ui/elements/Heading';
|
||||
import {
|
||||
type FindMyTransactionsQuery,
|
||||
TransactionStatus,
|
||||
useFindMyTransactionsQuery,
|
||||
} from '@/graphql/generated/output';
|
||||
import { convertPrice } from '@/utils/convert-price';
|
||||
import { formatDate } from '@/utils/format-date';
|
||||
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
export const TransactionsTable = () => {
|
||||
const t = useTranslations('dashboard.transactions');
|
||||
|
||||
const { data, loading: isLoadingTransactions } = useFindMyTransactionsQuery();
|
||||
const transactions = data?.findMyTransactions ?? [];
|
||||
|
||||
const transactionsColumns: ColumnDef<
|
||||
FindMyTransactionsQuery['findMyTransactions'][0]
|
||||
>[] = [
|
||||
{
|
||||
accessorKey: 'createdAt',
|
||||
header: t('columns.date'),
|
||||
cell: ({ row }) => formatDate(row.original.createdAt),
|
||||
},
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: t('columns.status'),
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
cell: ({ row }) => {
|
||||
const { status } = row.original;
|
||||
// eslint-disable-next-line no-useless-assignment
|
||||
let statusColor = '';
|
||||
|
||||
switch (status) {
|
||||
case TransactionStatus.Success:
|
||||
statusColor = 'text-green-500';
|
||||
|
||||
return (
|
||||
<div className={`py-1.5 ${statusColor}`}>
|
||||
{t('columns.success')}
|
||||
</div>
|
||||
);
|
||||
case TransactionStatus.Pending:
|
||||
statusColor = 'text-yellow-500';
|
||||
|
||||
return (
|
||||
<div className={`py-1.5 ${statusColor}`}>
|
||||
{t('columns.pending')}
|
||||
</div>
|
||||
);
|
||||
case TransactionStatus.Failed:
|
||||
statusColor = 'text-red-600';
|
||||
|
||||
return (
|
||||
<div className={`py-1.5 ${statusColor}`}>
|
||||
{t('columns.failed')}
|
||||
</div>
|
||||
);
|
||||
case TransactionStatus.Expired:
|
||||
statusColor = 'text-purple-500';
|
||||
|
||||
return (
|
||||
<div className={`py-1.5 ${statusColor}`}>
|
||||
{t('columns.expired')}
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
statusColor = 'text-foreground';
|
||||
|
||||
return (
|
||||
<div className={`py-1.5 ${statusColor}`}>
|
||||
{status}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'amount',
|
||||
header: t('columns.amount'),
|
||||
cell: ({ row }) => convertPrice(row.original.amount),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="lg:px-10">
|
||||
<Heading
|
||||
description={t('header.description')}
|
||||
title={t('header.heading')}
|
||||
/>
|
||||
|
||||
<div className="mt-5">
|
||||
{isLoadingTransactions
|
||||
? (
|
||||
<DataTableSkeleton />
|
||||
)
|
||||
: (
|
||||
<DataTable
|
||||
columns={transactionsColumns}
|
||||
data={transactions}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -72,7 +72,6 @@ export const ChangeAvatarForm = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return isLoadingProfile
|
||||
? (
|
||||
<ChangeAvatarFormSkeleton />
|
||||
|
||||
124
frontend/src/components/ui/common/Table.tsx
Normal file
124
frontend/src/components/ui/common/Table.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
import {
|
||||
type HTMLAttributes,
|
||||
type TdHTMLAttributes,
|
||||
type ThHTMLAttributes,
|
||||
forwardRef,
|
||||
} from 'react';
|
||||
|
||||
import { cn } from '@/utils/tw-merge';
|
||||
|
||||
const Table = forwardRef<HTMLTableElement, HTMLAttributes<HTMLTableElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div className="relative w-full overflow-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn('w-full caption-bottom text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
);
|
||||
Table.displayName = 'Table';
|
||||
|
||||
const TableHeader = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
||||
));
|
||||
TableHeader.displayName = 'TableHeader';
|
||||
|
||||
const TableBody = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={cn('[&_tr:last-child]:border-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableBody.displayName = 'TableBody';
|
||||
|
||||
const TableFooter = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableFooter.displayName = 'TableFooter';
|
||||
|
||||
const TableRow = forwardRef<
|
||||
HTMLTableRowElement,
|
||||
HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'border-b transition-colors hover:bg-card data-[state=selected]:bg-muted',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableRow.displayName = 'TableRow';
|
||||
|
||||
const TableHead = forwardRef<
|
||||
HTMLTableCellElement,
|
||||
ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableHead.displayName = 'TableHead';
|
||||
|
||||
const TableCell = forwardRef<
|
||||
HTMLTableCellElement,
|
||||
TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'px-4 py-2 align-middle [&:has([role=checkbox])]:pr-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCell.displayName = 'TableCell';
|
||||
|
||||
const TableCaption = forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={cn('mt-4 text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCaption.displayName = 'TableCaption';
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
};
|
||||
40
frontend/src/components/ui/elements/CopyButton.tsx
Normal file
40
frontend/src/components/ui/elements/CopyButton.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { Check, Copy } from 'lucide-react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Button } from '../common/Button';
|
||||
|
||||
type CopyButtonProps = {
|
||||
value?: string | null;
|
||||
};
|
||||
|
||||
export const CopyButton = ({ value }: CopyButtonProps) => {
|
||||
const t = useTranslations('components.copyButton');
|
||||
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
function onCopy() {
|
||||
if (!value) return;
|
||||
|
||||
setIsCopied(true);
|
||||
void navigator.clipboard.writeText(value);
|
||||
toast.success(t('successMessage'));
|
||||
setTimeout(() => {
|
||||
setIsCopied(false);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
const Icon = isCopied ? Check : Copy;
|
||||
|
||||
return (
|
||||
<Button
|
||||
disabled={!value || isCopied}
|
||||
size="lgIcon"
|
||||
variant="ghost"
|
||||
onClick={() => { onCopy(); }}
|
||||
>
|
||||
<Icon className="size-5" />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
99
frontend/src/components/ui/elements/DataTable.tsx
Normal file
99
frontend/src/components/ui/elements/DataTable.tsx
Normal file
@ -0,0 +1,99 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
type ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { Loader } from 'lucide-react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
import { Card } from '../common/Card';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '../common/Table';
|
||||
|
||||
type DataTableProps<TData, TValue> = {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
};
|
||||
|
||||
export const DataTable = <TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) => {
|
||||
const t = useTranslations('components.dataTable');
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef
|
||||
.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length
|
||||
? table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
: (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
className="h-24 text-center"
|
||||
colSpan={columns.length}
|
||||
>
|
||||
{t('notFound')}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DataTableSkeleton = () => (
|
||||
<div className="mx-auto mb-10 w-full max-w-(--breakpoint-2xl)">
|
||||
<Card className="mt-6 flex h-[500px] w-full items-center justify-center">
|
||||
<Loader className="size-8 animate-spin text-muted-foreground" />
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
@ -643,6 +643,48 @@ export type VerifyAccountMutationVariables = Exact<{
|
||||
|
||||
export type VerifyAccountMutation = { __typename?: 'Mutation', verifyAccount: { __typename?: 'AuthModel', message?: string | null, user?: { __typename?: 'UserModel', isEmailVerified: boolean } | null } };
|
||||
|
||||
export type ChangeChatSettingsMutationVariables = Exact<{
|
||||
data: ChangeChatSettingsInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type ChangeChatSettingsMutation = { __typename?: 'Mutation', changeChatSettings: { __typename?: 'StreamModel', id: string } };
|
||||
|
||||
export type SendChatMessageMutationVariables = Exact<{
|
||||
data: SendMessageInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type SendChatMessageMutation = { __typename?: 'Mutation', sendChatMessage: { __typename?: 'ChatMessageModel', streamId: string } };
|
||||
|
||||
export type CreateSponsorshipPlanMutationVariables = Exact<{
|
||||
data: CreatePlanInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateSponsorshipPlanMutation = { __typename?: 'Mutation', createSponsorshipPlan: { __typename?: 'PlanModel', id: string } };
|
||||
|
||||
export type RemoveSponsorshipPlanMutationVariables = Exact<{
|
||||
planId: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type RemoveSponsorshipPlanMutation = { __typename?: 'Mutation', removeSponsorshipPlan: { __typename?: 'PlanModel', id: string } };
|
||||
|
||||
export type MakePaymentMutationVariables = Exact<{
|
||||
planId: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MakePaymentMutation = { __typename?: 'Mutation', makePayment: { __typename?: 'MakePaymentModel', url: string } };
|
||||
|
||||
export type CreateIngressMutationVariables = Exact<{
|
||||
ingressType: Scalars['Float']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateIngressMutation = { __typename?: 'Mutation', createIngress: boolean };
|
||||
|
||||
export type ChangeEmailMutationVariables = Exact<{
|
||||
data: ChangeEmailInput;
|
||||
}>;
|
||||
@ -743,6 +785,31 @@ export type FindRecommendedChannelsQueryVariables = Exact<{ [key: string]: never
|
||||
|
||||
export type FindRecommendedChannelsQuery = { __typename?: 'Query', findRecommendedChannels: Array<{ __typename?: 'UserModel', id: string, name: string, avatar?: string | null, isVerified: boolean, stream: { __typename?: 'StreamModel', isLive: boolean } }> };
|
||||
|
||||
export type FindMyFollowersQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type FindMyFollowersQuery = { __typename?: 'Query', findMyFollowers: Array<{ __typename?: 'FollowModel', createdAt: any, follower: { __typename?: 'UserModel', name: string, avatar?: string | null, isVerified: boolean } }> };
|
||||
|
||||
export type FindMyFollowingsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type FindMyFollowingsQuery = { __typename?: 'Query', findMyFollowings: Array<{ __typename?: 'FollowModel', createdAt: any, followingId: string }> };
|
||||
|
||||
export type FindMySponsorshipPlansQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type FindMySponsorshipPlansQuery = { __typename?: 'Query', findMySponsorshipPlans: Array<{ __typename?: 'PlanModel', id: string, createdAt: any, title: string, price: number }> };
|
||||
|
||||
export type FindMySponsorsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type FindMySponsorsQuery = { __typename?: 'Query', findMySponsors: Array<{ __typename?: 'SubscriptionModel', expiresAt: any, user: { __typename?: 'UserModel', name: string, avatar?: string | null, isVerified: boolean }, plan: { __typename?: 'PlanModel', title: string } }> };
|
||||
|
||||
export type FindMyTransactionsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type FindMyTransactionsQuery = { __typename?: 'Query', findMyTransactions: Array<{ __typename?: 'TransactionModel', createdAt: any, status: TransactionStatus, amount: number }> };
|
||||
|
||||
export type FindCurrentSessionQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@ -761,7 +828,7 @@ export type FindUnreadNotificationsCountQuery = { __typename?: 'Query', findUnre
|
||||
export type FindProfileQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type FindProfileQuery = { __typename?: 'Query', findProfile: { __typename?: 'UserModel', avatar?: string | null, bio?: string | null, createdAt: any, email: string, id: string, name: string, updatedAt: any, isEmailVerified: boolean, isTotpEnabled: boolean, isVerified: boolean, displayName: string, socialLink: Array<{ __typename?: 'SocialLinkModel', title: string, url: string, position: number }>, notificationSettings?: { __typename?: 'NotificationSettingsModel', siteNotifications: boolean, telegramNotifications: boolean } | null } };
|
||||
export type FindProfileQuery = { __typename?: 'Query', findProfile: { __typename?: 'UserModel', avatar?: string | null, bio?: string | null, createdAt: any, email: string, id: string, name: string, updatedAt: any, isEmailVerified: boolean, isTotpEnabled: boolean, isVerified: boolean, displayName: string, socialLink: Array<{ __typename?: 'SocialLinkModel', title: string, url: string, position: number }>, notificationSettings?: { __typename?: 'NotificationSettingsModel', siteNotifications: boolean, telegramNotifications: boolean } | null, stream: { __typename?: 'StreamModel', serverUrl?: string | null, key?: string | null, isChatEnable: boolean, isChatFollowersOnly: boolean, isChatPremiumFollowersOnly: boolean } } };
|
||||
|
||||
export type FindSessionsByUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@ -1046,6 +1113,202 @@ export function useVerifyAccountMutation(baseOptions?: Apollo.MutationHookOption
|
||||
export type VerifyAccountMutationHookResult = ReturnType<typeof useVerifyAccountMutation>;
|
||||
export type VerifyAccountMutationResult = Apollo.MutationResult<VerifyAccountMutation>;
|
||||
export type VerifyAccountMutationOptions = Apollo.BaseMutationOptions<VerifyAccountMutation, VerifyAccountMutationVariables>;
|
||||
export const ChangeChatSettingsDocument = gql`
|
||||
mutation ChangeChatSettings($data: ChangeChatSettingsInput!) {
|
||||
changeChatSettings(data: $data) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type ChangeChatSettingsMutationFn = Apollo.MutationFunction<ChangeChatSettingsMutation, ChangeChatSettingsMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useChangeChatSettingsMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useChangeChatSettingsMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useChangeChatSettingsMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [changeChatSettingsMutation, { data, loading, error }] = useChangeChatSettingsMutation({
|
||||
* variables: {
|
||||
* data: // value for 'data'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useChangeChatSettingsMutation(baseOptions?: Apollo.MutationHookOptions<ChangeChatSettingsMutation, ChangeChatSettingsMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<ChangeChatSettingsMutation, ChangeChatSettingsMutationVariables>(ChangeChatSettingsDocument, options);
|
||||
}
|
||||
export type ChangeChatSettingsMutationHookResult = ReturnType<typeof useChangeChatSettingsMutation>;
|
||||
export type ChangeChatSettingsMutationResult = Apollo.MutationResult<ChangeChatSettingsMutation>;
|
||||
export type ChangeChatSettingsMutationOptions = Apollo.BaseMutationOptions<ChangeChatSettingsMutation, ChangeChatSettingsMutationVariables>;
|
||||
export const SendChatMessageDocument = gql`
|
||||
mutation SendChatMessage($data: SendMessageInput!) {
|
||||
sendChatMessage(data: $data) {
|
||||
streamId
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type SendChatMessageMutationFn = Apollo.MutationFunction<SendChatMessageMutation, SendChatMessageMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useSendChatMessageMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useSendChatMessageMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useSendChatMessageMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [sendChatMessageMutation, { data, loading, error }] = useSendChatMessageMutation({
|
||||
* variables: {
|
||||
* data: // value for 'data'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useSendChatMessageMutation(baseOptions?: Apollo.MutationHookOptions<SendChatMessageMutation, SendChatMessageMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<SendChatMessageMutation, SendChatMessageMutationVariables>(SendChatMessageDocument, options);
|
||||
}
|
||||
export type SendChatMessageMutationHookResult = ReturnType<typeof useSendChatMessageMutation>;
|
||||
export type SendChatMessageMutationResult = Apollo.MutationResult<SendChatMessageMutation>;
|
||||
export type SendChatMessageMutationOptions = Apollo.BaseMutationOptions<SendChatMessageMutation, SendChatMessageMutationVariables>;
|
||||
export const CreateSponsorshipPlanDocument = gql`
|
||||
mutation CreateSponsorshipPlan($data: CreatePlanInput!) {
|
||||
createSponsorshipPlan(data: $data) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type CreateSponsorshipPlanMutationFn = Apollo.MutationFunction<CreateSponsorshipPlanMutation, CreateSponsorshipPlanMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateSponsorshipPlanMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateSponsorshipPlanMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateSponsorshipPlanMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createSponsorshipPlanMutation, { data, loading, error }] = useCreateSponsorshipPlanMutation({
|
||||
* variables: {
|
||||
* data: // value for 'data'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateSponsorshipPlanMutation(baseOptions?: Apollo.MutationHookOptions<CreateSponsorshipPlanMutation, CreateSponsorshipPlanMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<CreateSponsorshipPlanMutation, CreateSponsorshipPlanMutationVariables>(CreateSponsorshipPlanDocument, options);
|
||||
}
|
||||
export type CreateSponsorshipPlanMutationHookResult = ReturnType<typeof useCreateSponsorshipPlanMutation>;
|
||||
export type CreateSponsorshipPlanMutationResult = Apollo.MutationResult<CreateSponsorshipPlanMutation>;
|
||||
export type CreateSponsorshipPlanMutationOptions = Apollo.BaseMutationOptions<CreateSponsorshipPlanMutation, CreateSponsorshipPlanMutationVariables>;
|
||||
export const RemoveSponsorshipPlanDocument = gql`
|
||||
mutation RemoveSponsorshipPlan($planId: String!) {
|
||||
removeSponsorshipPlan(planId: $planId) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type RemoveSponsorshipPlanMutationFn = Apollo.MutationFunction<RemoveSponsorshipPlanMutation, RemoveSponsorshipPlanMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useRemoveSponsorshipPlanMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useRemoveSponsorshipPlanMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useRemoveSponsorshipPlanMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [removeSponsorshipPlanMutation, { data, loading, error }] = useRemoveSponsorshipPlanMutation({
|
||||
* variables: {
|
||||
* planId: // value for 'planId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useRemoveSponsorshipPlanMutation(baseOptions?: Apollo.MutationHookOptions<RemoveSponsorshipPlanMutation, RemoveSponsorshipPlanMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<RemoveSponsorshipPlanMutation, RemoveSponsorshipPlanMutationVariables>(RemoveSponsorshipPlanDocument, options);
|
||||
}
|
||||
export type RemoveSponsorshipPlanMutationHookResult = ReturnType<typeof useRemoveSponsorshipPlanMutation>;
|
||||
export type RemoveSponsorshipPlanMutationResult = Apollo.MutationResult<RemoveSponsorshipPlanMutation>;
|
||||
export type RemoveSponsorshipPlanMutationOptions = Apollo.BaseMutationOptions<RemoveSponsorshipPlanMutation, RemoveSponsorshipPlanMutationVariables>;
|
||||
export const MakePaymentDocument = gql`
|
||||
mutation MakePayment($planId: String!) {
|
||||
makePayment(planId: $planId) {
|
||||
url
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type MakePaymentMutationFn = Apollo.MutationFunction<MakePaymentMutation, MakePaymentMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useMakePaymentMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useMakePaymentMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useMakePaymentMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [makePaymentMutation, { data, loading, error }] = useMakePaymentMutation({
|
||||
* variables: {
|
||||
* planId: // value for 'planId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useMakePaymentMutation(baseOptions?: Apollo.MutationHookOptions<MakePaymentMutation, MakePaymentMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<MakePaymentMutation, MakePaymentMutationVariables>(MakePaymentDocument, options);
|
||||
}
|
||||
export type MakePaymentMutationHookResult = ReturnType<typeof useMakePaymentMutation>;
|
||||
export type MakePaymentMutationResult = Apollo.MutationResult<MakePaymentMutation>;
|
||||
export type MakePaymentMutationOptions = Apollo.BaseMutationOptions<MakePaymentMutation, MakePaymentMutationVariables>;
|
||||
export const CreateIngressDocument = gql`
|
||||
mutation CreateIngress($ingressType: Float!) {
|
||||
createIngress(ingressType: $ingressType)
|
||||
}
|
||||
`;
|
||||
export type CreateIngressMutationFn = Apollo.MutationFunction<CreateIngressMutation, CreateIngressMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateIngressMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateIngressMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateIngressMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createIngressMutation, { data, loading, error }] = useCreateIngressMutation({
|
||||
* variables: {
|
||||
* ingressType: // value for 'ingressType'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateIngressMutation(baseOptions?: Apollo.MutationHookOptions<CreateIngressMutation, CreateIngressMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<CreateIngressMutation, CreateIngressMutationVariables>(CreateIngressDocument, options);
|
||||
}
|
||||
export type CreateIngressMutationHookResult = ReturnType<typeof useCreateIngressMutation>;
|
||||
export type CreateIngressMutationResult = Apollo.MutationResult<CreateIngressMutation>;
|
||||
export type CreateIngressMutationOptions = Apollo.BaseMutationOptions<CreateIngressMutation, CreateIngressMutationVariables>;
|
||||
export const ChangeEmailDocument = gql`
|
||||
mutation ChangeEmail($data: ChangeEmailInput!) {
|
||||
changeEmail(data: $data) {
|
||||
@ -1558,6 +1821,220 @@ export type FindRecommendedChannelsQueryHookResult = ReturnType<typeof useFindRe
|
||||
export type FindRecommendedChannelsLazyQueryHookResult = ReturnType<typeof useFindRecommendedChannelsLazyQuery>;
|
||||
export type FindRecommendedChannelsSuspenseQueryHookResult = ReturnType<typeof useFindRecommendedChannelsSuspenseQuery>;
|
||||
export type FindRecommendedChannelsQueryResult = Apollo.QueryResult<FindRecommendedChannelsQuery, FindRecommendedChannelsQueryVariables>;
|
||||
export const FindMyFollowersDocument = gql`
|
||||
query FindMyFollowers {
|
||||
findMyFollowers {
|
||||
createdAt
|
||||
follower {
|
||||
name
|
||||
avatar
|
||||
isVerified
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useFindMyFollowersQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useFindMyFollowersQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useFindMyFollowersQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useFindMyFollowersQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useFindMyFollowersQuery(baseOptions?: Apollo.QueryHookOptions<FindMyFollowersQuery, FindMyFollowersQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<FindMyFollowersQuery, FindMyFollowersQueryVariables>(FindMyFollowersDocument, options);
|
||||
}
|
||||
export function useFindMyFollowersLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<FindMyFollowersQuery, FindMyFollowersQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<FindMyFollowersQuery, FindMyFollowersQueryVariables>(FindMyFollowersDocument, options);
|
||||
}
|
||||
export function useFindMyFollowersSuspenseQuery(baseOptions?: Apollo.SkipToken | Apollo.SuspenseQueryHookOptions<FindMyFollowersQuery, FindMyFollowersQueryVariables>) {
|
||||
const options = baseOptions === Apollo.skipToken ? baseOptions : {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useSuspenseQuery<FindMyFollowersQuery, FindMyFollowersQueryVariables>(FindMyFollowersDocument, options);
|
||||
}
|
||||
export type FindMyFollowersQueryHookResult = ReturnType<typeof useFindMyFollowersQuery>;
|
||||
export type FindMyFollowersLazyQueryHookResult = ReturnType<typeof useFindMyFollowersLazyQuery>;
|
||||
export type FindMyFollowersSuspenseQueryHookResult = ReturnType<typeof useFindMyFollowersSuspenseQuery>;
|
||||
export type FindMyFollowersQueryResult = Apollo.QueryResult<FindMyFollowersQuery, FindMyFollowersQueryVariables>;
|
||||
export const FindMyFollowingsDocument = gql`
|
||||
query FindMyFollowings {
|
||||
findMyFollowings {
|
||||
createdAt
|
||||
followingId
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useFindMyFollowingsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useFindMyFollowingsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useFindMyFollowingsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useFindMyFollowingsQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useFindMyFollowingsQuery(baseOptions?: Apollo.QueryHookOptions<FindMyFollowingsQuery, FindMyFollowingsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<FindMyFollowingsQuery, FindMyFollowingsQueryVariables>(FindMyFollowingsDocument, options);
|
||||
}
|
||||
export function useFindMyFollowingsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<FindMyFollowingsQuery, FindMyFollowingsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<FindMyFollowingsQuery, FindMyFollowingsQueryVariables>(FindMyFollowingsDocument, options);
|
||||
}
|
||||
export function useFindMyFollowingsSuspenseQuery(baseOptions?: Apollo.SkipToken | Apollo.SuspenseQueryHookOptions<FindMyFollowingsQuery, FindMyFollowingsQueryVariables>) {
|
||||
const options = baseOptions === Apollo.skipToken ? baseOptions : {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useSuspenseQuery<FindMyFollowingsQuery, FindMyFollowingsQueryVariables>(FindMyFollowingsDocument, options);
|
||||
}
|
||||
export type FindMyFollowingsQueryHookResult = ReturnType<typeof useFindMyFollowingsQuery>;
|
||||
export type FindMyFollowingsLazyQueryHookResult = ReturnType<typeof useFindMyFollowingsLazyQuery>;
|
||||
export type FindMyFollowingsSuspenseQueryHookResult = ReturnType<typeof useFindMyFollowingsSuspenseQuery>;
|
||||
export type FindMyFollowingsQueryResult = Apollo.QueryResult<FindMyFollowingsQuery, FindMyFollowingsQueryVariables>;
|
||||
export const FindMySponsorshipPlansDocument = gql`
|
||||
query FindMySponsorshipPlans {
|
||||
findMySponsorshipPlans {
|
||||
id
|
||||
createdAt
|
||||
title
|
||||
price
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useFindMySponsorshipPlansQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useFindMySponsorshipPlansQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useFindMySponsorshipPlansQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useFindMySponsorshipPlansQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useFindMySponsorshipPlansQuery(baseOptions?: Apollo.QueryHookOptions<FindMySponsorshipPlansQuery, FindMySponsorshipPlansQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<FindMySponsorshipPlansQuery, FindMySponsorshipPlansQueryVariables>(FindMySponsorshipPlansDocument, options);
|
||||
}
|
||||
export function useFindMySponsorshipPlansLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<FindMySponsorshipPlansQuery, FindMySponsorshipPlansQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<FindMySponsorshipPlansQuery, FindMySponsorshipPlansQueryVariables>(FindMySponsorshipPlansDocument, options);
|
||||
}
|
||||
export function useFindMySponsorshipPlansSuspenseQuery(baseOptions?: Apollo.SkipToken | Apollo.SuspenseQueryHookOptions<FindMySponsorshipPlansQuery, FindMySponsorshipPlansQueryVariables>) {
|
||||
const options = baseOptions === Apollo.skipToken ? baseOptions : {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useSuspenseQuery<FindMySponsorshipPlansQuery, FindMySponsorshipPlansQueryVariables>(FindMySponsorshipPlansDocument, options);
|
||||
}
|
||||
export type FindMySponsorshipPlansQueryHookResult = ReturnType<typeof useFindMySponsorshipPlansQuery>;
|
||||
export type FindMySponsorshipPlansLazyQueryHookResult = ReturnType<typeof useFindMySponsorshipPlansLazyQuery>;
|
||||
export type FindMySponsorshipPlansSuspenseQueryHookResult = ReturnType<typeof useFindMySponsorshipPlansSuspenseQuery>;
|
||||
export type FindMySponsorshipPlansQueryResult = Apollo.QueryResult<FindMySponsorshipPlansQuery, FindMySponsorshipPlansQueryVariables>;
|
||||
export const FindMySponsorsDocument = gql`
|
||||
query FindMySponsors {
|
||||
findMySponsors {
|
||||
expiresAt
|
||||
user {
|
||||
name
|
||||
avatar
|
||||
isVerified
|
||||
}
|
||||
plan {
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useFindMySponsorsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useFindMySponsorsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useFindMySponsorsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useFindMySponsorsQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useFindMySponsorsQuery(baseOptions?: Apollo.QueryHookOptions<FindMySponsorsQuery, FindMySponsorsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<FindMySponsorsQuery, FindMySponsorsQueryVariables>(FindMySponsorsDocument, options);
|
||||
}
|
||||
export function useFindMySponsorsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<FindMySponsorsQuery, FindMySponsorsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<FindMySponsorsQuery, FindMySponsorsQueryVariables>(FindMySponsorsDocument, options);
|
||||
}
|
||||
export function useFindMySponsorsSuspenseQuery(baseOptions?: Apollo.SkipToken | Apollo.SuspenseQueryHookOptions<FindMySponsorsQuery, FindMySponsorsQueryVariables>) {
|
||||
const options = baseOptions === Apollo.skipToken ? baseOptions : {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useSuspenseQuery<FindMySponsorsQuery, FindMySponsorsQueryVariables>(FindMySponsorsDocument, options);
|
||||
}
|
||||
export type FindMySponsorsQueryHookResult = ReturnType<typeof useFindMySponsorsQuery>;
|
||||
export type FindMySponsorsLazyQueryHookResult = ReturnType<typeof useFindMySponsorsLazyQuery>;
|
||||
export type FindMySponsorsSuspenseQueryHookResult = ReturnType<typeof useFindMySponsorsSuspenseQuery>;
|
||||
export type FindMySponsorsQueryResult = Apollo.QueryResult<FindMySponsorsQuery, FindMySponsorsQueryVariables>;
|
||||
export const FindMyTransactionsDocument = gql`
|
||||
query FindMyTransactions {
|
||||
findMyTransactions {
|
||||
createdAt
|
||||
status
|
||||
amount
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useFindMyTransactionsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useFindMyTransactionsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useFindMyTransactionsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useFindMyTransactionsQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useFindMyTransactionsQuery(baseOptions?: Apollo.QueryHookOptions<FindMyTransactionsQuery, FindMyTransactionsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<FindMyTransactionsQuery, FindMyTransactionsQueryVariables>(FindMyTransactionsDocument, options);
|
||||
}
|
||||
export function useFindMyTransactionsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<FindMyTransactionsQuery, FindMyTransactionsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<FindMyTransactionsQuery, FindMyTransactionsQueryVariables>(FindMyTransactionsDocument, options);
|
||||
}
|
||||
export function useFindMyTransactionsSuspenseQuery(baseOptions?: Apollo.SkipToken | Apollo.SuspenseQueryHookOptions<FindMyTransactionsQuery, FindMyTransactionsQueryVariables>) {
|
||||
const options = baseOptions === Apollo.skipToken ? baseOptions : {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useSuspenseQuery<FindMyTransactionsQuery, FindMyTransactionsQueryVariables>(FindMyTransactionsDocument, options);
|
||||
}
|
||||
export type FindMyTransactionsQueryHookResult = ReturnType<typeof useFindMyTransactionsQuery>;
|
||||
export type FindMyTransactionsLazyQueryHookResult = ReturnType<typeof useFindMyTransactionsLazyQuery>;
|
||||
export type FindMyTransactionsSuspenseQueryHookResult = ReturnType<typeof useFindMyTransactionsSuspenseQuery>;
|
||||
export type FindMyTransactionsQueryResult = Apollo.QueryResult<FindMyTransactionsQuery, FindMyTransactionsQueryVariables>;
|
||||
export const FindCurrentSessionDocument = gql`
|
||||
query FindCurrentSession {
|
||||
findCurrentSession {
|
||||
@ -1716,6 +2193,13 @@ export const FindProfileDocument = gql`
|
||||
siteNotifications
|
||||
telegramNotifications
|
||||
}
|
||||
stream {
|
||||
serverUrl
|
||||
key
|
||||
isChatEnable
|
||||
isChatFollowersOnly
|
||||
isChatPremiumFollowersOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
mutation ChangeChatSettings($data: ChangeChatSettingsInput!) {
|
||||
changeChatSettings(data: $data) {
|
||||
id
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
mutation SendChatMessage($data: SendMessageInput!) {
|
||||
sendChatMessage(data: $data) {
|
||||
streamId
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
mutation CreateSponsorshipPlan($data: CreatePlanInput!) {
|
||||
createSponsorshipPlan(data: $data) {
|
||||
id
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
mutation RemoveSponsorshipPlan($planId: String!) {
|
||||
removeSponsorshipPlan(planId: $planId) {
|
||||
id
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
mutation MakePayment($planId: String!) {
|
||||
makePayment(planId: $planId) {
|
||||
url
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
mutation CreateIngress($ingressType: Float!) {
|
||||
createIngress(ingressType: $ingressType)
|
||||
}
|
||||
10
frontend/src/graphql/queries/follow/FindMyFollowers.graphql
Normal file
10
frontend/src/graphql/queries/follow/FindMyFollowers.graphql
Normal file
@ -0,0 +1,10 @@
|
||||
query FindMyFollowers {
|
||||
findMyFollowers {
|
||||
createdAt
|
||||
follower {
|
||||
name
|
||||
avatar
|
||||
isVerified
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
query FindMyFollowings {
|
||||
findMyFollowings {
|
||||
createdAt
|
||||
followingId
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
query FindMySponsorshipPlans {
|
||||
findMySponsorshipPlans {
|
||||
id
|
||||
createdAt
|
||||
title
|
||||
price
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
query FindMySponsors {
|
||||
findMySponsors {
|
||||
expiresAt
|
||||
user {
|
||||
name
|
||||
avatar
|
||||
isVerified
|
||||
}
|
||||
plan {
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
query FindMyTransactions {
|
||||
findMyTransactions {
|
||||
createdAt
|
||||
status
|
||||
amount
|
||||
}
|
||||
}
|
||||
@ -20,5 +20,12 @@ query FindProfile {
|
||||
siteNotifications
|
||||
telegramNotifications
|
||||
}
|
||||
stream {
|
||||
serverUrl
|
||||
key
|
||||
isChatEnable
|
||||
isChatFollowersOnly
|
||||
isChatPremiumFollowersOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,15 +2,17 @@ import { ApolloClient, InMemoryCache, split } from '@apollo/client';
|
||||
import { WebSocketLink } from '@apollo/client/link/ws';
|
||||
import { getMainDefinition } from '@apollo/client/utilities';
|
||||
import createUploadLink from 'apollo-upload-client/createUploadLink.mjs';
|
||||
import { OperationTypeNode } from 'graphql/language';
|
||||
import { Kind } from 'graphql/language/kinds';
|
||||
|
||||
import { SERVER_URL, WEBSOCKET_URL } from './constants/url.constants';
|
||||
|
||||
const httpLink = createUploadLink({
|
||||
uri: SERVER_URL,
|
||||
credentials: 'include',
|
||||
// headers: {
|
||||
// 'apollo-require-preflight': 'true',
|
||||
// },
|
||||
headers: {
|
||||
'apollo-require-preflight': 'true',
|
||||
},
|
||||
});
|
||||
|
||||
const wsLink = new WebSocketLink({
|
||||
@ -25,8 +27,8 @@ const splitLink = split(
|
||||
const definition = getMainDefinition(query);
|
||||
|
||||
return (
|
||||
definition.kind === 'OperationDefinition'
|
||||
&& definition.operation === 'subscription'
|
||||
definition.kind === Kind.OPERATION_DEFINITION
|
||||
&& definition.operation === OperationTypeNode.SUBSCRIPTION
|
||||
);
|
||||
},
|
||||
wsLink,
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export const SERVER_URL = process.env.NEXT_PUBLIC_SERVER_URL as string;
|
||||
export const WEBSOCKET_URL = process.env.NEXT_PUBLIC_WEBSOCKET_URL as string
|
||||
export const WEBSOCKET_URL = process.env.NEXT_PUBLIC_WEBSOCKET_URL as string;
|
||||
export const MEDIA_URL = process.env.NEXT_PUBLIC_MEDIA_URL as string;
|
||||
|
||||
11
frontend/src/schemas/chat/change-chat.settings.schema.ts
Normal file
11
frontend/src/schemas/chat/change-chat.settings.schema.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const changeChatSettingsSchema = z.object({
|
||||
isChatEnable: z.boolean(),
|
||||
isChatFollowersOnly: z.boolean(),
|
||||
isChatPremiumFollowersOnly: z.boolean(),
|
||||
});
|
||||
|
||||
export type TypeChangeChatSettingsSchema = z.infer<
|
||||
typeof changeChatSettingsSchema
|
||||
>;
|
||||
7
frontend/src/schemas/chat/send-message.schema.ts
Normal file
7
frontend/src/schemas/chat/send-message.schema.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const sendMessageSchema = z.object({
|
||||
text: z.string().min(1),
|
||||
});
|
||||
|
||||
export type TypeSendMessageSchema = z.infer<typeof sendMessageSchema>;
|
||||
9
frontend/src/schemas/plan/create-plan.schema.ts
Normal file
9
frontend/src/schemas/plan/create-plan.schema.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const createPlanSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
price: z.number().positive(),
|
||||
});
|
||||
|
||||
export type TypeCreatePlanSchema = z.infer<typeof createPlanSchema>;
|
||||
12
frontend/src/schemas/stream/create-ingress.schema.ts
Normal file
12
frontend/src/schemas/stream/create-ingress.schema.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export enum IngressType {
|
||||
RTMP = 0,
|
||||
WHIP = 1,
|
||||
}
|
||||
|
||||
export const createIngressSchema = z.object({
|
||||
ingressType: z.enum(IngressType),
|
||||
});
|
||||
|
||||
export type TypeCreateIngressSchema = z.infer<typeof createIngressSchema>;
|
||||
6
frontend/src/utils/convert-price.ts
Normal file
6
frontend/src/utils/convert-price.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export function convertPrice(price: number) {
|
||||
return price.toLocaleString('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: 'RUB',
|
||||
});
|
||||
}
|
||||
@ -15,7 +15,7 @@
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@apollo/client@3.13.9":
|
||||
"@apollo/client@3.13.9", "@apollo/client@^3.8.0":
|
||||
version "3.13.9"
|
||||
resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.13.9.tgz#311e0df1734aaa5598e01a292bb7982df49cc062"
|
||||
integrity sha512-RStSzQfL1XwL6/NWd7W8avhGQYTgPCtJ+qHkkTTSj9Upp3VVm6Oppv81YWdXG1FgEpDPW4hvCrTUELdcC4inCQ==
|
||||
@ -2169,6 +2169,18 @@
|
||||
postcss "^8.4.41"
|
||||
tailwindcss "4.1.11"
|
||||
|
||||
"@tanstack/react-table@8.21.3":
|
||||
version "8.21.3"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.21.3.tgz#2c38c747a5731c1a07174fda764b9c2b1fb5e91b"
|
||||
integrity sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==
|
||||
dependencies:
|
||||
"@tanstack/table-core" "8.21.3"
|
||||
|
||||
"@tanstack/table-core@8.21.3":
|
||||
version "8.21.3"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.21.3.tgz#2977727d8fc8dfa079112d9f4d4c019110f1732c"
|
||||
integrity sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==
|
||||
|
||||
"@theguild/federation-composition@^0.19.0":
|
||||
version "0.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@theguild/federation-composition/-/federation-composition-0.19.1.tgz#b3907bfcbdbd69d4628a3b1270936615f56b904f"
|
||||
@ -2213,11 +2225,25 @@
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@types/apollo-upload-client@18.0.0":
|
||||
version "18.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/apollo-upload-client/-/apollo-upload-client-18.0.0.tgz#1f7e2ff0c0e6508bc1bd19b2340339c7abd117c3"
|
||||
integrity sha512-cMgITNemktxasqvp6jiPj15dv84n3FTMvMoYBP1+xonDS+0l6JygIJrj2LJh85rShRzTOOkrElrAsCXXARa3KA==
|
||||
dependencies:
|
||||
"@apollo/client" "^3.8.0"
|
||||
"@types/extract-files" "*"
|
||||
graphql "14 - 16"
|
||||
|
||||
"@types/estree@^1.0.6":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
||||
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
||||
|
||||
"@types/extract-files@*":
|
||||
version "13.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/extract-files/-/extract-files-13.0.1.tgz#3ec057a3fa25f778245a76a17271d23b71ee31d7"
|
||||
integrity sha512-/fRbzc2lAd7jDJSSnxWiUyXWjdUZZ4HbISLJzVgt1AvrdOa7U49YRPcvuCUywkmURZ7uwJOheDjx19itbQ5KvA==
|
||||
|
||||
"@types/js-yaml@^4.0.0":
|
||||
version "4.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2"
|
||||
@ -4271,7 +4297,7 @@ graphql-ws@^6.0.6:
|
||||
resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-6.0.6.tgz#e9c9ff85f4ddb5bbe6faa2721c0d27e6c11746d2"
|
||||
integrity sha512-zgfER9s+ftkGKUZgc0xbx8T7/HMO4AV5/YuYiFc+AtgcO5T0v8AxYYNQ+ltzuzDZgNkYJaFspm5MMYLjQzrkmw==
|
||||
|
||||
graphql@16.11.0:
|
||||
"graphql@14 - 16", graphql@16.11.0:
|
||||
version "16.11.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633"
|
||||
integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user