mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
chore(web): remove unused code
This commit is contained in:
parent
652ef8a427
commit
f1fb971cdd
20 changed files with 1 additions and 329 deletions
|
|
@ -1,9 +0,0 @@
|
|||
const createObjectURLMock = vi.fn();
|
||||
|
||||
// eslint-disable-next-line unicorn/no-top-level-side-effects
|
||||
Object.defineProperty(URL, 'createObjectURL', {
|
||||
writable: true,
|
||||
value: createObjectURLMock,
|
||||
});
|
||||
|
||||
export { createObjectURLMock };
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
import type { Attachment } from 'svelte/attachments';
|
||||
|
||||
export interface DragAndDropOptions {
|
||||
index: number;
|
||||
onDragStart?: (index: number) => void;
|
||||
onDragEnter?: (index: number) => void;
|
||||
onDrop?: (e: DragEvent, index: number) => void;
|
||||
onDragEnd?: () => void;
|
||||
isDragging?: boolean;
|
||||
isDragOver?: boolean;
|
||||
}
|
||||
|
||||
export function dragAndDrop(options: DragAndDropOptions): Attachment {
|
||||
return (node: Element) => {
|
||||
const element = node as HTMLElement;
|
||||
const { index, onDragStart, onDragEnter, onDrop, onDragEnd, isDragging, isDragOver } = options;
|
||||
|
||||
const isFormElement = (el: HTMLElement) => {
|
||||
return ['INPUT', 'TEXTAREA', 'SELECT'].includes(el.tagName);
|
||||
};
|
||||
|
||||
const handleDragStart = (e: DragEvent) => {
|
||||
// Prevent drag if it originated from an input, textarea, or select element
|
||||
const target = e.target as HTMLElement;
|
||||
if (isFormElement(target)) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
onDragStart?.(index);
|
||||
};
|
||||
|
||||
const handleDragEnter = () => {
|
||||
onDragEnter?.(index);
|
||||
};
|
||||
|
||||
const handleDragOver = (e: DragEvent) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const handleDrop = (e: DragEvent) => {
|
||||
onDrop?.(e, index);
|
||||
};
|
||||
|
||||
const handleDragEnd = () => {
|
||||
onDragEnd?.();
|
||||
};
|
||||
|
||||
// Disable draggable when focusing on form elements (fixes Firefox input interaction)
|
||||
const handleFocusIn = (e: FocusEvent) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (isFormElement(target)) {
|
||||
element.setAttribute('draggable', 'false');
|
||||
}
|
||||
};
|
||||
|
||||
const handleFocusOut = (e: FocusEvent) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (isFormElement(target)) {
|
||||
element.setAttribute('draggable', 'true');
|
||||
}
|
||||
};
|
||||
|
||||
// Update classes based on drag state
|
||||
const updateClasses = (dragging: boolean, dragOver: boolean) => {
|
||||
// Remove all drag-related classes first
|
||||
element.classList.remove('opacity-50', 'border-light-500', 'border-solid');
|
||||
|
||||
// Add back only the active ones
|
||||
if (dragging) {
|
||||
element.classList.add('opacity-50');
|
||||
}
|
||||
|
||||
if (dragOver) {
|
||||
element.classList.add('border-light-500', 'border-solid');
|
||||
element.classList.remove('border-transparent');
|
||||
} else {
|
||||
element.classList.add('border-transparent');
|
||||
}
|
||||
};
|
||||
|
||||
element.setAttribute('draggable', 'true');
|
||||
element.setAttribute('role', 'button');
|
||||
element.setAttribute('tabindex', '0');
|
||||
|
||||
element.addEventListener('dragstart', handleDragStart);
|
||||
element.addEventListener('dragenter', handleDragEnter);
|
||||
element.addEventListener('dragover', handleDragOver);
|
||||
element.addEventListener('drop', handleDrop);
|
||||
element.addEventListener('dragend', handleDragEnd);
|
||||
element.addEventListener('focusin', handleFocusIn);
|
||||
element.addEventListener('focusout', handleFocusOut);
|
||||
|
||||
updateClasses(isDragging || false, isDragOver || false);
|
||||
|
||||
return () => {
|
||||
element.removeEventListener('dragstart', handleDragStart);
|
||||
element.removeEventListener('dragenter', handleDragEnter);
|
||||
element.removeEventListener('dragover', handleDragOver);
|
||||
element.removeEventListener('drop', handleDrop);
|
||||
element.removeEventListener('dragend', handleDragEnd);
|
||||
element.removeEventListener('focusin', handleFocusIn);
|
||||
element.removeEventListener('focusout', handleFocusOut);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
<script lang="ts">
|
||||
import type { CropAspectRatio } from '$lib/managers/edit/transform-manager.svelte';
|
||||
import { Button, Icon, type Color } from '@immich/ui';
|
||||
|
||||
interface Props {
|
||||
size: {
|
||||
icon: string;
|
||||
name: CropAspectRatio;
|
||||
viewBox: string;
|
||||
rotate?: boolean;
|
||||
};
|
||||
selectedSize: CropAspectRatio;
|
||||
rotateHorizontal: boolean;
|
||||
selectType: (size: CropAspectRatio) => void;
|
||||
}
|
||||
|
||||
let { size, selectedSize, rotateHorizontal, selectType }: Props = $props();
|
||||
|
||||
let isSelected = $derived(selectedSize === size.name);
|
||||
let buttonColor = $derived<Color>(isSelected ? 'primary' : 'secondary');
|
||||
|
||||
let rotatedTitle = $derived((title: string, toRotate: boolean) => {
|
||||
let sides = title.split(':');
|
||||
if (toRotate) {
|
||||
sides.reverse();
|
||||
}
|
||||
return sides.join(':');
|
||||
});
|
||||
|
||||
let toRotate = $derived((def: boolean | undefined) => {
|
||||
if (def === false) {
|
||||
return false;
|
||||
}
|
||||
return (def && !rotateHorizontal) || (!def && rotateHorizontal);
|
||||
});
|
||||
</script>
|
||||
|
||||
<li>
|
||||
<Button shape="round" color={buttonColor} class="flex-col gap-1" size="small" onclick={() => selectType(size.name)}>
|
||||
<Icon size="1.75em" icon={size.icon} viewBox={size.viewBox} class={toRotate(size.rotate) ? 'rotate-90' : ''} />
|
||||
<span>{rotatedTitle(size.name, rotateHorizontal)}</span>
|
||||
</Button>
|
||||
</li>
|
||||
|
|
@ -7,9 +7,6 @@ import { getDimensions } from '$lib/utils/asset-utils';
|
|||
import { normalizeTransformEdits } from '$lib/utils/editor';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
|
||||
export type CropAspectRatio =
|
||||
'1:1' | '16:9' | '4:3' | '3:2' | '7:5' | '9:16' | '3:4' | '2:3' | '5:7' | 'free' | 'reset';
|
||||
|
||||
type Region = {
|
||||
x: number;
|
||||
y: number;
|
||||
|
|
|
|||
|
|
@ -47,11 +47,6 @@ export interface Viewport {
|
|||
height: number;
|
||||
}
|
||||
|
||||
export type ViewportXY = Viewport & {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export interface AddAsset {
|
||||
type: 'add';
|
||||
values: TimelineAsset[];
|
||||
|
|
@ -82,12 +77,6 @@ export type ScrubberMonth = {
|
|||
title: string;
|
||||
};
|
||||
|
||||
export type TimelineManagerLayoutOptions = {
|
||||
rowHeight?: number;
|
||||
headerHeight?: number;
|
||||
gap?: number;
|
||||
};
|
||||
|
||||
export interface UpdateGeometryOptions {
|
||||
invalidateHeight: boolean;
|
||||
noDefer?: boolean;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import type { TimelineAsset } from './types';
|
||||
|
||||
export const assetSnapshot = (asset: TimelineAsset): TimelineAsset => $state.snapshot(asset);
|
||||
export const assetsSnapshot = (assets: TimelineAsset[]) => assets.map((asset) => $state.snapshot(asset));
|
||||
|
||||
export function filterIsInOrNearViewport<T extends { isInOrNearViewport: boolean }>(items: T[]) {
|
||||
|
|
|
|||
|
|
@ -38,12 +38,6 @@ const asQueryString = (
|
|||
return items.length === 0 ? '' : `?${items.join('&')}`;
|
||||
};
|
||||
|
||||
const DOCS_BASE = 'https://docs.immich.app';
|
||||
|
||||
export const Docs = {
|
||||
duplicates: () => `${DOCS_BASE}/features/duplicates-utility`,
|
||||
};
|
||||
|
||||
export const Route = {
|
||||
// auth
|
||||
login: (params?: { continue?: string; autoLaunch?: 0 | 1 }) => '/auth/login' + asQueryString(params),
|
||||
|
|
|
|||
|
|
@ -286,16 +286,3 @@ export const handleDeleteAlbum = async (album: AlbumResponseDto, options?: { pro
|
|||
export const handleDownloadAlbum = async (album: AlbumResponseDto) => {
|
||||
await downloadArchive(`${album.albumName}.zip`, { albumId: album.id });
|
||||
};
|
||||
|
||||
export const handleConfirmAlbumDelete = async (album: AlbumResponseDto) => {
|
||||
const $t = await getFormatter();
|
||||
const confirmation =
|
||||
album.albumName.length > 0
|
||||
? $t('album_delete_confirmation', { values: { album: album.albumName } })
|
||||
: $t('unnamed_album_delete_confirmation');
|
||||
|
||||
const description = $t('album_delete_confirmation_description');
|
||||
const prompt = `${confirmation} ${description}`;
|
||||
|
||||
return modalManager.showDialog({ prompt });
|
||||
};
|
||||
|
|
|
|||
|
|
@ -151,10 +151,6 @@ export const handleRestoreUserAdmin = async (user: UserAdminResponseDto) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const handleNavigateUserAdmin = async (user: UserAdminResponseDto) => {
|
||||
await goto(`/admin/users/${user.id}`);
|
||||
};
|
||||
|
||||
// TODO move password reset server-side
|
||||
const generatePassword = (length: number = 16) => {
|
||||
let generatedPassword = '';
|
||||
|
|
|
|||
|
|
@ -70,11 +70,6 @@ export interface PlacesViewSettings {
|
|||
};
|
||||
}
|
||||
|
||||
export interface SidebarSettings {
|
||||
people: boolean;
|
||||
sharing: boolean;
|
||||
}
|
||||
|
||||
export enum SortOrder {
|
||||
Asc = 'asc',
|
||||
Desc = 'desc',
|
||||
|
|
|
|||
|
|
@ -24,12 +24,6 @@ export enum SlideshowMetadataOverlayMode {
|
|||
Full = 'full',
|
||||
}
|
||||
|
||||
export const slideshowLookCssMapping: Record<SlideshowLook, string> = {
|
||||
[SlideshowLook.Contain]: 'object-contain',
|
||||
[SlideshowLook.Cover]: 'object-cover',
|
||||
[SlideshowLook.BlurredBackground]: 'object-contain',
|
||||
};
|
||||
|
||||
function createSlideshowStore() {
|
||||
const restartState = writable<boolean>(false);
|
||||
const stopState = writable<boolean>(false);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import type { ActionItem } from '@immich/ui';
|
|||
import type { DateTime } from 'luxon';
|
||||
import type { SvelteSet } from 'svelte/reactivity';
|
||||
import { MediaType } from '$lib/constants';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
|
||||
export type LatLng = { lng: number; lat: number };
|
||||
|
||||
|
|
@ -39,13 +38,6 @@ export enum OnboardingRole {
|
|||
USER = 'user',
|
||||
}
|
||||
|
||||
export type AssetControlContext = {
|
||||
// Wrap assets in a function, because context isn't reactive.
|
||||
getAssets: () => TimelineAsset[]; // All assets includes partners' assets
|
||||
getOwnedAssets: () => TimelineAsset[]; // Only assets owned by the user
|
||||
clearSelect: () => void;
|
||||
};
|
||||
|
||||
export type SearchCameraFilter = {
|
||||
make?: string;
|
||||
model?: string;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ export type OnUndoDelete = (assets: TimelineAsset[]) => void;
|
|||
export type OnRestore = (ids: string[]) => void;
|
||||
export type OnLink = (assets: { still: TimelineAsset; motion: TimelineAsset }) => void;
|
||||
export type OnUnlink = (assets: { still: TimelineAsset; motion: TimelineAsset }) => void;
|
||||
export type OnAddToAlbum = (ids: string[], albumId: string) => void;
|
||||
export type OnArchive = (ids: string[], visibility: AssetVisibility) => void;
|
||||
export type OnFavorite = (ids: string[], favorite: boolean) => void;
|
||||
export type OnStack = (result: StackResponse) => void;
|
||||
|
|
|
|||
|
|
@ -78,12 +78,3 @@ export const getAlbumDateRange = (album: { startDate?: string; endDate?: string
|
|||
*/
|
||||
export const asLocalTimeISO = (date: DateTime<true>) =>
|
||||
(date.setZone('utc', { keepLocalTime: true }) as DateTime<true>).toISO();
|
||||
|
||||
const days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
||||
|
||||
type DayOfWeek = (typeof days)[number];
|
||||
export const dayOfWeek = (day: DayOfWeek, options?: { locale?: string; style?: 'long' | 'short' | 'narrow' }) => {
|
||||
const fmt = new Intl.DateTimeFormat(options?.locale, { weekday: options?.style ?? 'long', timeZone: 'UTC' });
|
||||
// 2021-08-01 is a Sunday
|
||||
return fmt.format(new Date(Date.UTC(2021, 7, 1 + days.indexOf(day))));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,27 +20,6 @@ import { ExecutorQueue } from '$lib/utils/executor-queue';
|
|||
import { asQueryString } from '$lib/utils/shared-links';
|
||||
import { handleError } from './handle-error';
|
||||
|
||||
export const addDummyItems = () => {
|
||||
uploadAssetsStore.addItem({ id: 'asset-0', file: { name: 'asset0.jpg', size: 123_456 } as File });
|
||||
uploadAssetsStore.updateItem('asset-0', { state: UploadState.PENDING });
|
||||
uploadAssetsStore.addItem({ id: 'asset-1', file: { name: 'asset1.jpg', size: 123_456 } as File });
|
||||
uploadAssetsStore.updateItem('asset-1', { state: UploadState.STARTED });
|
||||
uploadAssetsStore.updateProgress('asset-1', 75, 100);
|
||||
uploadAssetsStore.addItem({ id: 'asset-2', file: { name: 'asset2.jpg', size: 123_456 } as File });
|
||||
uploadAssetsStore.updateItem('asset-2', { state: UploadState.ERROR, error: new Error('Internal server error') });
|
||||
uploadAssetsStore.addItem({ id: 'asset-3', file: { name: 'asset3.jpg', size: 123_456 } as File });
|
||||
uploadAssetsStore.updateItem('asset-3', { state: UploadState.DUPLICATED, assetId: 'asset-2' });
|
||||
uploadAssetsStore.addItem({ id: 'asset-4', file: { name: 'asset3.jpg', size: 123_456 } as File });
|
||||
uploadAssetsStore.updateItem('asset-4', { state: UploadState.DUPLICATED, assetId: 'asset-2', isTrashed: true });
|
||||
uploadAssetsStore.addItem({ id: 'asset-10', file: { name: 'asset3.jpg', size: 123_456 } as File });
|
||||
uploadAssetsStore.updateItem('asset-10', { state: UploadState.DONE });
|
||||
uploadAssetsStore.track('error');
|
||||
uploadAssetsStore.track('success');
|
||||
uploadAssetsStore.track('duplicate');
|
||||
};
|
||||
|
||||
// addDummyItems();
|
||||
|
||||
export const uploadExecutionQueue = new ExecutorQueue({ concurrency: 2 });
|
||||
|
||||
type FilePickerParam = { multiple?: boolean; extensions?: string[] };
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ import { getAssetRatio } from '$lib/utils/asset-utils';
|
|||
import { isTimelineAsset, isTimelineAssets } from '$lib/utils/timeline-util';
|
||||
import { TUNABLES } from '$lib/utils/tunables';
|
||||
|
||||
export type getJustifiedLayoutFromAssetsFunction = typeof getJustifiedLayoutFromAssets;
|
||||
|
||||
const useWasm = TUNABLES.LAYOUT.WASM;
|
||||
|
||||
export type CommonJustifiedLayout = {
|
||||
|
|
@ -115,13 +113,6 @@ export function justifiedLayout(assets: (TimelineAsset | AssetResponseDto)[], op
|
|||
return new Adapter(result);
|
||||
}
|
||||
|
||||
export const emptyGeometry = () =>
|
||||
new Adapter({
|
||||
containerHeight: 0,
|
||||
widowCount: 0,
|
||||
boxes: [],
|
||||
});
|
||||
|
||||
export type CommonPosition = {
|
||||
top: number;
|
||||
left: number;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ export const isPhotosRoute = (route?: string | null) => !!route?.startsWith('/(u
|
|||
const isSharedLinkSlugRoute = (route?: string | null) => !!route?.startsWith('/(user)/s/[slug]');
|
||||
export const isSharedLinkRoute = (route?: string | null) =>
|
||||
!!route?.startsWith('/(user)/share/[key]') || isSharedLinkSlugRoute(route);
|
||||
export const isSearchRoute = (route?: string | null) => !!route?.startsWith('/(user)/search');
|
||||
export const isAlbumsRoute = (route?: string | null) => !!route?.startsWith('/(user)/albums/[albumId=id]');
|
||||
export const isPeopleRoute = (route?: string | null) => !!route?.startsWith('/(user)/people/[personId]');
|
||||
export const isLockedFolderRoute = (route?: string | null) => !!route?.startsWith('/(user)/locked');
|
||||
|
|
@ -149,12 +148,6 @@ export const clearQueryParam = async (queryParam: string, url: URL) => {
|
|||
await goto(url, { keepFocus: true });
|
||||
};
|
||||
|
||||
export const getQueryValue = (queryKey: string) => {
|
||||
const url = location.href;
|
||||
const urlObject = new URL(url);
|
||||
return urlObject.searchParams.get(queryKey);
|
||||
};
|
||||
|
||||
export const setQueryValue = async (queryKey: string, queryValue: string) => {
|
||||
const url = location.href;
|
||||
const urlObject = new URL(url);
|
||||
|
|
|
|||
|
|
@ -4,40 +4,6 @@ import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
|||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { fromTimelinePlainDateTime } from '$lib/utils/timeline-util';
|
||||
|
||||
/**
|
||||
* Calculate thumbnail size based on number of assets and viewport width
|
||||
* @param assetCount Number of assets in the view
|
||||
* @param viewWidth viewport width
|
||||
* @returns thumbnail size
|
||||
*/
|
||||
export function getThumbnailSize(assetCount: number, viewWidth: number): number {
|
||||
if (assetCount < 6) {
|
||||
return Math.min(320, Math.floor(viewWidth / assetCount - assetCount));
|
||||
}
|
||||
|
||||
if (viewWidth > 600) {
|
||||
return viewWidth / 7 - 7;
|
||||
}
|
||||
|
||||
if (viewWidth > 400) {
|
||||
return viewWidth / 4 - 6;
|
||||
}
|
||||
|
||||
if (viewWidth > 300) {
|
||||
return viewWidth / 2 - 6;
|
||||
}
|
||||
|
||||
if (viewWidth > 200) {
|
||||
return viewWidth / 2 - 6;
|
||||
}
|
||||
|
||||
if (viewWidth > 100) {
|
||||
return viewWidth / 1 - 6;
|
||||
}
|
||||
|
||||
return 300;
|
||||
}
|
||||
|
||||
export const getAltText = derived(t, ($t) => {
|
||||
return (asset: TimelineAsset) => {
|
||||
const date = fromTimelinePlainDateTime(asset.localDateTime).toJSDate().toLocaleString(get(locale), {
|
||||
|
|
|
|||
|
|
@ -33,36 +33,12 @@ export type ScrubberListener = (scrubberData: {
|
|||
export const fromISODateTime = (isoDateTime: string, timeZone: string): DateTime<true> =>
|
||||
DateTime.fromISO(isoDateTime, { zone: timeZone, locale: get(locale) }) as DateTime<true>;
|
||||
|
||||
export const fromISODateTimeToObject = (isoDateTime: string, timeZone: string): TimelineDateTime =>
|
||||
(fromISODateTime(isoDateTime, timeZone) as DateTime<true>).toObject();
|
||||
|
||||
// used for AssetResponseDto.localDateTime, amongst others
|
||||
export const fromISODateTimeUTC = (isoDateTimeUtc: string) => fromISODateTime(isoDateTimeUtc, 'UTC');
|
||||
|
||||
export const fromISODateTimeUTCToObject = (isoDateTimeUtc: string): TimelineDateTime =>
|
||||
(fromISODateTimeUTC(isoDateTimeUtc) as DateTime<true>).toObject();
|
||||
|
||||
// used to create equivalent of AssetResponseDto.localDateTime in UTC, but without timezone information
|
||||
export const fromISODateTimeTruncateTZToObject = (
|
||||
isoDateTimeUtc: string,
|
||||
timeZone: string | undefined,
|
||||
): TimelineDateTime =>
|
||||
(
|
||||
fromISODateTime(isoDateTimeUtc, timeZone ?? 'UTC').setZone('UTC', { keepLocalTime: true }) as DateTime<true>
|
||||
).toObject();
|
||||
|
||||
// Used to derive a local date time from an ISO string and a UTC offset in hours
|
||||
export const fromISODateTimeWithOffsetToObject = (isoDateTimeUtc: string, utcOffsetHours: number): TimelineDateTime => {
|
||||
const utcDateTime = fromISODateTimeUTC(isoDateTimeUtc);
|
||||
|
||||
// Apply the offset to get the local time
|
||||
// Note: offset is in hours (may be fractional), positive for east of UTC, negative for west
|
||||
const localDateTime = utcDateTime.plus({ hours: utcOffsetHours });
|
||||
|
||||
// Return as plain object (keeping the local time but in UTC zone context)
|
||||
return (localDateTime.setZone('UTC', { keepLocalTime: true }) as DateTime<true>).toObject();
|
||||
};
|
||||
|
||||
export const getTimes = (isoDateTimeUtc: string, localUtcOffsetHours: number) => {
|
||||
const utcDateTime = fromISODateTimeUTC(isoDateTimeUtc);
|
||||
const fileCreatedAt = (utcDateTime as DateTime<true>).toObject();
|
||||
|
|
|
|||
|
|
@ -1,16 +1,7 @@
|
|||
import { faker } from '@faker-js/faker';
|
||||
import { UserAvatarColor, UserStatus, type UserAdminResponseDto, type UserResponseDto } from '@immich/sdk';
|
||||
import { UserAvatarColor, UserStatus, type UserAdminResponseDto } from '@immich/sdk';
|
||||
import { Sync } from 'factory.ts';
|
||||
|
||||
export const userFactory = Sync.makeFactory<UserResponseDto>({
|
||||
id: Sync.each(() => faker.string.uuid()),
|
||||
email: Sync.each(() => faker.internet.email()),
|
||||
name: Sync.each(() => faker.person.fullName()),
|
||||
profileImagePath: '',
|
||||
avatarColor: UserAvatarColor.Primary,
|
||||
profileChangedAt: Sync.each(() => faker.date.recent().toISOString()),
|
||||
});
|
||||
|
||||
export const userAdminFactory = Sync.makeFactory<UserAdminResponseDto>({
|
||||
id: Sync.each(() => faker.string.uuid()),
|
||||
email: Sync.each(() => faker.internet.email()),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue