fix and update
This commit is contained in:
@@ -1007,6 +1007,7 @@ export async function apiFetch<T>(
|
||||
? { 'Content-Type': 'application/json' }
|
||||
: {}),
|
||||
...(resolvedToken ? { Authorization: `Bearer ${resolvedToken}` } : {}),
|
||||
...(behavior.silent ? { 'X-Id-Passive-Activity': '1' } : {}),
|
||||
...(options.headers as Record<string, string> | undefined)
|
||||
};
|
||||
|
||||
@@ -1075,8 +1076,14 @@ export interface MediaAccessResponse {
|
||||
expiresAt: string;
|
||||
}
|
||||
|
||||
export async function fetchDocumentPhotoUrl(userId: string, storageKey: string, token?: string | null) {
|
||||
export async function fetchDocumentPhotoUrl(
|
||||
userId: string,
|
||||
storageKey: string,
|
||||
token?: string | null,
|
||||
fileName?: string
|
||||
) {
|
||||
const query = new URLSearchParams({ storageKey });
|
||||
if (fileName) query.set('fileName', fileName);
|
||||
return apiFetch<MediaAccessResponse>(`/media/users/${userId}/documents/photo-url?${query.toString()}`, {}, token);
|
||||
}
|
||||
|
||||
|
||||
76
apps/frontend/lib/document-attachments.ts
Normal file
76
apps/frontend/lib/document-attachments.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
export type DocumentAttachmentMeta = {
|
||||
fileName?: string;
|
||||
contentType?: string;
|
||||
};
|
||||
|
||||
export const DOCUMENT_FILE_ACCEPT =
|
||||
'image/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.rtf,.odt,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
||||
|
||||
const EXTENSION_TO_MIME: Record<string, string> = {
|
||||
jpg: 'image/jpeg',
|
||||
jpeg: 'image/jpeg',
|
||||
png: 'image/png',
|
||||
webp: 'image/webp',
|
||||
gif: 'image/gif',
|
||||
pdf: 'application/pdf',
|
||||
doc: 'application/msword',
|
||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
xls: 'application/vnd.ms-excel',
|
||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
ppt: 'application/vnd.ms-powerpoint',
|
||||
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
txt: 'text/plain',
|
||||
rtf: 'application/rtf',
|
||||
odt: 'application/vnd.oasis.opendocument.text'
|
||||
};
|
||||
|
||||
export function resolveDocumentFileContentType(file: File) {
|
||||
const fromType = file.type?.trim();
|
||||
if (fromType && fromType !== 'application/octet-stream') {
|
||||
return fromType;
|
||||
}
|
||||
const extension = file.name.split('.').pop()?.toLowerCase() ?? '';
|
||||
return EXTENSION_TO_MIME[extension] ?? 'application/octet-stream';
|
||||
}
|
||||
|
||||
export function inferAttachmentContentType(storageKey: string, meta?: DocumentAttachmentMeta) {
|
||||
if (meta?.contentType) return meta.contentType;
|
||||
const extension = storageKey.split('.').pop()?.toLowerCase() ?? '';
|
||||
return EXTENSION_TO_MIME[extension] ?? 'application/octet-stream';
|
||||
}
|
||||
|
||||
export function isImageAttachment(contentType: string) {
|
||||
return contentType.startsWith('image/');
|
||||
}
|
||||
|
||||
export function isPdfAttachment(contentType: string) {
|
||||
return contentType === 'application/pdf';
|
||||
}
|
||||
|
||||
export function attachmentKindLabel(contentType: string) {
|
||||
if (isImageAttachment(contentType)) return 'Изображение';
|
||||
if (isPdfAttachment(contentType)) return 'PDF';
|
||||
if (contentType.includes('word') || contentType === 'application/msword') return 'Word';
|
||||
if (contentType.includes('excel') || contentType === 'application/vnd.ms-excel') return 'Excel';
|
||||
if (contentType.includes('powerpoint') || contentType === 'application/vnd.ms-powerpoint') return 'PowerPoint';
|
||||
if (contentType === 'text/plain') return 'Текст';
|
||||
return 'Файл';
|
||||
}
|
||||
|
||||
export function attachmentDisplayName(storageKey: string, meta?: DocumentAttachmentMeta) {
|
||||
if (meta?.fileName?.trim()) return meta.fileName.trim();
|
||||
const parts = storageKey.split('/');
|
||||
return parts[parts.length - 1] ?? 'Файл';
|
||||
}
|
||||
|
||||
export function attachmentFileExtension(contentType: string, fileName?: string) {
|
||||
const fromName = fileName?.split('.').pop()?.toUpperCase();
|
||||
if (fromName && fromName.length <= 5) return fromName;
|
||||
if (isPdfAttachment(contentType)) return 'PDF';
|
||||
if (contentType.includes('word')) return 'DOC';
|
||||
if (contentType.includes('excel')) return 'XLS';
|
||||
if (contentType.includes('powerpoint')) return 'PPT';
|
||||
if (contentType === 'text/plain') return 'TXT';
|
||||
if (isImageAttachment(contentType)) return 'IMG';
|
||||
return 'FILE';
|
||||
}
|
||||
@@ -290,9 +290,34 @@ export function parseDocumentPhotos(metadata: Record<string, unknown>): string[]
|
||||
return [];
|
||||
}
|
||||
|
||||
export function withDocumentPhotos(metadata: Record<string, unknown>, photoStorageKeys: string[]) {
|
||||
export type DocumentAttachmentMeta = {
|
||||
fileName?: string;
|
||||
contentType?: string;
|
||||
};
|
||||
|
||||
export function parseAttachmentMeta(metadata: Record<string, unknown>): Record<string, DocumentAttachmentMeta> {
|
||||
const raw = metadata.attachmentMeta;
|
||||
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
||||
return {};
|
||||
}
|
||||
const result: Record<string, DocumentAttachmentMeta> = {};
|
||||
for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) continue;
|
||||
const entry = value as Record<string, unknown>;
|
||||
result[key] = {
|
||||
fileName: typeof entry.fileName === 'string' ? entry.fileName : undefined,
|
||||
contentType: typeof entry.contentType === 'string' ? entry.contentType : undefined
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function withDocumentPhotos(metadata: Record<string, unknown>, photoStorageKeys: string[], attachmentMeta?: Record<string, DocumentAttachmentMeta>) {
|
||||
const next: Record<string, unknown> = { ...metadata, photoStorageKeys };
|
||||
delete next.photoStorageKey;
|
||||
if (attachmentMeta && Object.keys(attachmentMeta).length > 0) {
|
||||
next.attachmentMeta = attachmentMeta;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
43
apps/frontend/lib/selected-family-storage.ts
Normal file
43
apps/frontend/lib/selected-family-storage.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export const SELECTED_FAMILY_STORAGE_KEY = 'id-selected-family-group-id';
|
||||
|
||||
export function readStoredFamilyGroupId(): string | null {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
const stored = window.localStorage.getItem(SELECTED_FAMILY_STORAGE_KEY);
|
||||
return stored?.trim() ? stored : null;
|
||||
}
|
||||
|
||||
export function writeStoredFamilyGroupId(groupId: string | null) {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
if (groupId) {
|
||||
window.localStorage.setItem(SELECTED_FAMILY_STORAGE_KEY, groupId);
|
||||
} else {
|
||||
window.localStorage.removeItem(SELECTED_FAMILY_STORAGE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveFamilyGroupId(
|
||||
groups: Array<{ id: string }>,
|
||||
selectedGroupId: string | null,
|
||||
isSelectionHydrated: boolean
|
||||
): string | null {
|
||||
if (!groups.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const candidates: Array<string | null> = [selectedGroupId];
|
||||
if (!isSelectionHydrated) {
|
||||
candidates.unshift(readStoredFamilyGroupId());
|
||||
}
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (candidate && groups.some((group) => group.id === candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return isSelectionHydrated ? groups[0]!.id : null;
|
||||
}
|
||||
Reference in New Issue
Block a user