export type RequestStatus = 'pending' | 'in_review' | 'approved' | 'rejected' | 'cancelled' | 'closed'; export declare const REQUEST_STATUSES: RequestStatus[]; export interface RequestActor { userId?: string; name: string; email: string; phone?: string; handle?: string; avatarUrl?: string; avatarThumbnail?: string; } export interface AppRequest { id: string; appId: string; orgId: string; type: string; status: RequestStatus; title: string; fields: Record; metadata?: Record; tags?: string[]; priority?: string; submitter: RequestActor; assignee?: RequestActor; /** Copiado del RequestTypeConfig al crear — no lo manda quien responde. */ entityRef?: EntityRef; dueAt?: string; createdAt: string; updatedAt: string; closedAt?: string; } export interface RequestComment { commentId: string; requestId: string; author: RequestActor; body: string; createdAt: string; } export type FieldOption = FormFieldOption; export type FieldSchemaDef = FormFieldSchemaDef; /** * Tipo de pregunta de escala corta con emoji (3-5 caras). NO es uno de los * tipos que `FormSchemaBuilderService.buildForm` sabe rutear a `val-form` — * un tipo sin mapeo cae a texto plano ahí. Renderizarlo requiere el widget * dedicado `val-emoji-rating`, nunca el form genérico. Ver ADR-092. */ export declare const FIELD_TYPE_EMOJI_RATING = "EMOJI_RATING"; /** Identifica la entidad de dominio a la que pertenece un tipo de solicitud * (un evento, una adopción, una receta). Opcional — la mayoría de los tipos * (tickets de soporte, postulaciones) no la necesitan porque el tipo YA es la * entidad. Mismo shape que el `EntityRef` de feedback/chat — no se importa * ninguno de esos paquetes para esto: es un struct de 2 campos, replicarlo es * más barato que acoplar dominios de negocio por un tipo trivial. */ export interface EntityRef { entityType: string; entityId: string; } /** Quién puede leer un tipo de solicitud (y sus solicitudes públicas). */ export type RequestVisibility = 'PUBLIC' | 'AUTHENTICATED' | 'ORG' | 'INTERNAL'; export interface RequestTypeConfig { typeId: string; appId: string; orgId: string; /** Nombre del tipo, escrito por la organización: no se traduce. */ label: string; /** Bajada del enunciado (ej. encuestas). Label ya cubre el título. */ subtitle?: string; entityRef?: EntityRef; createdBy?: RequestActor; /** * true = cada envío se agrega, no se revisa uno por uno (ej. encuestas). * false/undefined (default) = comportamiento histórico: se revisa cada * solicitud (Chesed adopciones, Okhelia recetas). */ skipReview?: boolean; /** * true = el formulario NO pide nombre ni correo a quien responde sin sesión. * Si hacen falta, se agregan como pregunta — eso es configuración de la * encuesta, no un peaje que el formulario cobra por su cuenta. */ skipSubmitterIdentity?: boolean; fieldSchema: FieldSchemaDef[]; allowAnonymous: boolean; /** * Quién puede leer/enviar este tipo (debe coincidir con el backend): * - PUBLIC cualquiera, incluso sin login (anónimo si allowAnonymous). * - AUTHENTICATED cualquier usuario logueado + verificado, cross-org. * - ORG/INTERNAL solo miembros de la org dueña. Default ORG. */ visibility?: RequestVisibility; createdAt: string; updatedAt: string; } export interface CreateRequestPayload { type: string; title: string; fields?: Record; tags?: string[]; priority?: string; /** Para solicitudes anónimas */ submitter?: RequestActor; /** * El `?i=` del link que le llegó por correo a quien responde. Con token, la * invitación se consume y no admite una segunda respuesta; sin token (el QR * abierto de un evento) se responde sin límite. */ inviteToken?: string; dueAt?: string; } export interface UpdateRequestPayload { title?: string; fields?: Record; tags?: string[]; priority?: string; assignee?: RequestActor; dueAt?: string; } export interface TransitionPayload { status: RequestStatus; } export interface AddCommentPayload { body: string; } export interface ListRequestsParams { type?: string; status?: string; limit?: number; nextToken?: string; } export interface ListRequestsResponse { requests: AppRequest[]; nextToken?: string; count: number; } export interface ListCommentsResponse { comments: RequestComment[]; count: number; } export interface ListRequestTypesResponse { types: RequestTypeConfig[]; } export interface CreateRequestTypePayload { typeId?: string; /** Nombre del tipo, escrito por la organización: no se traduce. */ label: string; subtitle?: string; entityRef?: EntityRef; skipReview?: boolean; /** * true = el formulario NO pide nombre ni correo a quien responde sin sesión. * Si hacen falta, se agregan como pregunta — eso es configuración de la * encuesta, no un peaje que el formulario cobra por su cuenta. */ skipSubmitterIdentity?: boolean; fieldSchema: FieldSchemaDef[]; allowAnonymous: boolean; visibility?: RequestVisibility; } export interface UpdateRequestTypePayload { /** Nombre del tipo, escrito por la organización: no se traduce. */ label: string; subtitle?: string; entityRef?: EntityRef; skipReview?: boolean; /** * true = el formulario NO pide nombre ni correo a quien responde sin sesión. * Si hacen falta, se agregan como pregunta — eso es configuración de la * encuesta, no un peaje que el formulario cobra por su cuenta. */ skipSubmitterIdentity?: boolean; fieldSchema: FieldSchemaDef[]; allowAnonymous: boolean; visibility?: RequestVisibility; } import type { FieldOption as FormFieldOption, FieldSchemaDef as FormFieldSchemaDef } from '../forms/types';