Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 

80 строки
2.8 KiB

  1. import type { Card, CardType } from '@/types';
  2. export const CARD_LIMITS = {
  3. title: 200,
  4. shortDescription: 500,
  5. fullContent: 20000,
  6. actionUrl: 2000,
  7. } as const;
  8. const VALID_CARD_TYPES: readonly CardType[] = [
  9. 'INFO_PAGE',
  10. 'EXTERNAL_LINK',
  11. 'IMAGE_GALLERY',
  12. 'SERVICE_REQUEST',
  13. 'BOOK',
  14. ] as const;
  15. const ALLOWED_URL_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:']);
  16. export type ValidationError = {
  17. field: string;
  18. message: string;
  19. limit?: number;
  20. actual?: number;
  21. };
  22. export type ValidationResult = {
  23. valid: boolean;
  24. errors: ValidationError[];
  25. };
  26. function strLen(v: unknown): number {
  27. return typeof v === 'string' ? v.length : 0;
  28. }
  29. export function validateCard(card: Partial<Card>): ValidationResult {
  30. const errors: ValidationError[] = [];
  31. if (typeof card.title !== 'string' || card.title.trim().length === 0) {
  32. errors.push({ field: 'title', message: 'Il titolo è obbligatorio' });
  33. } else if (card.title.length > CARD_LIMITS.title) {
  34. errors.push({ field: 'title', message: 'Titolo troppo lungo', limit: CARD_LIMITS.title, actual: card.title.length });
  35. }
  36. if (card.shortDescription !== undefined && typeof card.shortDescription !== 'string') {
  37. errors.push({ field: 'shortDescription', message: 'Tipo non valido' });
  38. } else if (strLen(card.shortDescription) > CARD_LIMITS.shortDescription) {
  39. errors.push({ field: 'shortDescription', message: 'Descrizione breve troppo lunga', limit: CARD_LIMITS.shortDescription, actual: strLen(card.shortDescription) });
  40. }
  41. if (card.fullContent !== undefined && typeof card.fullContent !== 'string') {
  42. errors.push({ field: 'fullContent', message: 'Tipo non valido' });
  43. } else if (strLen(card.fullContent) > CARD_LIMITS.fullContent) {
  44. errors.push({ field: 'fullContent', message: 'Contenuto troppo lungo', limit: CARD_LIMITS.fullContent, actual: strLen(card.fullContent) });
  45. }
  46. if (card.actionUrl !== undefined && card.actionUrl !== '') {
  47. if (typeof card.actionUrl !== 'string') {
  48. errors.push({ field: 'actionUrl', message: 'Tipo non valido' });
  49. } else if (card.actionUrl.length > CARD_LIMITS.actionUrl) {
  50. errors.push({ field: 'actionUrl', message: 'URL troppo lungo', limit: CARD_LIMITS.actionUrl, actual: card.actionUrl.length });
  51. } else {
  52. try {
  53. const parsed = new URL(card.actionUrl);
  54. if (!ALLOWED_URL_SCHEMES.has(parsed.protocol)) {
  55. errors.push({ field: 'actionUrl', message: `Schema URL non ammesso (${parsed.protocol}). Usa http, https, mailto o tel.` });
  56. }
  57. } catch {
  58. errors.push({ field: 'actionUrl', message: 'URL non valido' });
  59. }
  60. }
  61. }
  62. if (card.cardType !== undefined && !VALID_CARD_TYPES.includes(card.cardType as CardType)) {
  63. errors.push({ field: 'cardType', message: `Tipo card non valido: ${String(card.cardType)}` });
  64. }
  65. return { valid: errors.length === 0, errors };
  66. }