@iblai/iblai-js 2.2.4 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2,7 +2,7 @@ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import React__default, { useState, useEffect, forwardRef, createElement as createElement$3, useLayoutEffect, createContext, useContext, useMemo, useCallback, useReducer, useRef, useImperativeHandle, isValidElement, Children, PureComponent, cloneElement, Component as Component$1, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
|
|
4
4
|
import * as dataLayer from '@iblai/data-layer';
|
|
5
|
-
import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useDeleteGlobalMemoryMutation, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useDeleteApiKeyMutation, useGetApiKeysQuery, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useGetLlmsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useUpdateTenantMetadataMutation, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation,
|
|
5
|
+
import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useDeleteGlobalMemoryMutation, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useGetApplicationFormsQuery, useGetApplicationFormDetailQuery, useCreateApplicationFormMutation, useUpdateApplicationFormMutation, useGetApplicationAdminDetailQuery, useInviteApplicationCandidateMutation, useLazyGetApplicationAttachmentFileQuery, useCreateApplicationWaiverMutation, useDeleteApplicationWaiverMutation, useSubmitApplicationOverrideMutation, useUpdateApplicationPlacementMutation, useDeleteApplicationCourseAssignmentMutation, useCreateApplicationNoteMutation, useUpdateApplicationNoteMutation, useDeleteApplicationNoteMutation, useTransitionApplicationMutation, useLinkApplicationCandidateMutation, useCreateApplicationPlacementsMutation, useGetCustomMentorsQuery, useCreateApplicationCourseAssignmentsMutation, useGetApplicationBlocksQuery, useDeleteApplicationBlockMutation, useGetApplicationsPipelineStatsQuery, useGetApplicationsPipelineQuery, useDeleteApiKeyMutation, useGetApiKeysQuery, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useGetLlmsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useUpdateTenantMetadataMutation, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useGetAiSearchMentorsQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useGetVectorDocumentsQuery, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useDeleteMentorMemoryMutation, useUpdateMentorMemoryMutation, useCreateMentorMemoryMutation, useLazyGetConnectedServiceAuthUrlQuery, useStarMentorMutation, useUnstarMentorMutation, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorSettingsQuery, useEditMentorMutation, useUploadLightLogoMutation, useUploadDarkLogoMutation, useUpdatePlatformInfoMutation, LOGO_ENDPOINTS, useCreateEvalDatasetMutation, useAddEvalDatasetItemsMutation, useUploadEvalDatasetCsvMutation, useDeleteEvalDatasetItemMutation, useListEvalDatasetItemsQuery, evalTotalPages, useListEvalDatasetsQuery, useGetUserProjectsQuery, useCreateSessionIdMutation, useGetMentorCategoriesQuery, useGetTenantChatPrivacyConfigQuery as useGetTenantChatPrivacyConfigQuery$1, useGetCallConfigurationsQuery, useCreateCallConfigurationMutation, useUpdateCallConfigurationMutation, chatPrivacyApiSlice, useDeleteMentorMutation, useForkMentorMutation, useGetToolsQuery, useLazyGetMCPServersQuery, useOauthFindMutation, useLazyStartOAuthFlowQuery, useCreateMCPServerMutation, usePartialUpdateMCPServerMutation, useCreateMCPServerConnectionMutation, usePatchMCPServerConnectionMutation, useGetConnectedServicesQuery, useGetMCPServersQuery, useGetMCPServerConnectionsQuery, useUpdateMCPServerMutation, useDeleteMCPServerMutation, useEditMentorJsonMutation, useDisconnectServiceMutation, useGetPromptCategoriesQuery, PRIVACY_ACTIONS, PRIVACY_ENTITY_TYPES, useGetVoicesQuery, useGetVoiceQuery, useGetMentorPublicSettingsQuery, useGetChatHistoryFilterQuery, useGetChatHistoryQuery, useGetMentorSummariesQuery, useGetConversationMemoriesQuery, useGetSupportTicketsQuery, useGetSupportTicketMessagesQuery, useCreateSupportTicketMessageMutation, usePatchSupportTicketMutation, useCloseSupportTicketMutation, useCreateLtiMentorMutation, useUpdateLtiMentorMutation, useGetLtiMentorsQuery, useCreateLtiKeyMutation, useUpdateLtiKeyMutation, useDeleteLtiKeyMutation, useGetLtiKeysQuery, useCreateLtiToolMutation, useUpdateLtiToolMutation, useGetLtiToolsQuery, useGetPlatformInfoQuery, useCreatePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useCreateRedirectTokenMutation, useGetShareableLinkQuery, useCreateShareableLinkMutation, useUpdateShareableLinkMutation, useCreateDisclaimerMutation, useUpdateDisclaimerMutation, useGetDisclaimersQuery, useUpdateRbacMentorAccessMutation, useGetRbacMentorAccessListQuery, useStartEvalRunMutation, useEvaluateEvalRunMutation, useGetEvalRunQuery, useDeleteEvalScoreMutation, useListEvalRunJudgesQuery, useCreateEvalScoreMutation, useDeleteEvalRunMutation, useExportEvalRunMutation, useListEvalRunsQuery, useLazyGetEvalRunQuery, evaluationsApiSlice, EVALUATIONS_TAGS, useGetPersonnalizedMentorsQuery, useCreateCallCredentialsMutation, useGetGuidedPromptsQuery, useUpdateChatSessionSharedMutation, useUpdateMessageFeedbackMutation, useLazyGetPromptsSearchQuery, useLazyGetGuidedPromptsQuery, useGetPublicMentorsQuery, useGetPeriodicAgentsQuery, useGetPeriodicAgentLogsListQuery, useCreatePeriodicAgentMutation, useDeletePeriodicAgentMutation, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
|
|
6
6
|
import { getInitials, useTenantMetadata, WithPermissions, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages, useUsername, selectStreaming, isLoggedIn, TOOLS, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, selectRbacPermissions, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, removeFile, chatInputSliceActions, chatActions, WithFormPermissions, useTenantContext, TimeTracker, advancedTabsProperties, defaultSessionIds, pickWritableFields, ANONYMOUS_USERNAME as ANONYMOUS_USERNAME$3, redirectToAuthSpaJoinTenant, selectActiveTab, useAxdToken, useWelcomeMessage, markdownToPlainText, useCachedSessionId, use402ErrorCheck, selectTokenEnabled, selectToken, useServiceWorker, useUserAgreement, useAdvancedChat, sendMessageToParentWebsite, useMentorTools, useFileDragDrop, eventBus, RemoteEvents, selectEnableChatActionsPopup, advancedTabs, isInIframe, getAuthSpaJoinUrl, addMessage, clearFiles, MENTOR_VISIBILITY } from '@iblai/web-utils';
|
|
7
7
|
import { useDispatch, useSelector } from 'react-redux';
|
|
8
8
|
import { toast } from 'sonner';
|
|
@@ -100020,7 +100020,9 @@ var profileAdmin$3 = {
|
|
|
100020
100020
|
rolesDescription: "An RBAC role is a named bundle of permissions (actions applied to resources in policies).",
|
|
100021
100021
|
policiesDescription: "An RBAC policy grants a role's permissions to a set of users or groups on a set of resources. Resources are hierarchical: granting on /platforms/{pk}/mentors/ covers every agent, while /platforms/{pk}/mentors/{pk}/ scopes to one.",
|
|
100022
100022
|
teamsDescription: "A team is a curated set of users that you can share with others — e.g., grant another user permission to view the team's analytics or send notifications to them.",
|
|
100023
|
-
alertsDescription: "An alert pairs a team of watched users with one or more watchers who get notified when monitored events occur for a watched user. Each watcher subscribes to its own subset of events."
|
|
100023
|
+
alertsDescription: "An alert pairs a team of watched users with one or more watchers who get notified when monitored events occur for a watched user. Each watcher subscribes to its own subset of events.",
|
|
100024
|
+
applicationsTab: "Applications",
|
|
100025
|
+
applicationsTabDescription: "Review applications to your organization and edit the questions applicants answer when they apply."
|
|
100024
100026
|
};
|
|
100025
100027
|
var profileAutoRechargeModal$3 = {
|
|
100026
100028
|
title: "Manage Usage",
|
|
@@ -102276,7 +102278,306 @@ var platformNavbar$3 = {
|
|
|
102276
102278
|
search: "Search",
|
|
102277
102279
|
searchPlaceholder: "Search",
|
|
102278
102280
|
logIn: "Log in",
|
|
102279
|
-
signUpForFree: "Sign up for free"
|
|
102281
|
+
signUpForFree: "Sign up for free",
|
|
102282
|
+
placementExamsAriaLabel: "Placement exams ({count} available)",
|
|
102283
|
+
placementExamsHeader: "Placement exams",
|
|
102284
|
+
placementExamFallback: "Placement exam"
|
|
102285
|
+
};
|
|
102286
|
+
var placementExam$3 = {
|
|
102287
|
+
title: "Placement exam",
|
|
102288
|
+
back: "Back"
|
|
102289
|
+
};
|
|
102290
|
+
var applyApplyWizard$3 = {
|
|
102291
|
+
application: "Application",
|
|
102292
|
+
selectAnOption: "Select an option",
|
|
102293
|
+
stepProgress: "Step {current} of {total}",
|
|
102294
|
+
reviewAndSubmit: "Review and submit",
|
|
102295
|
+
checkYourAnswers: "Check your answers, then submit your application.",
|
|
102296
|
+
bySubmitting: "By submitting, you confirm the information provided is accurate.",
|
|
102297
|
+
student: "Student",
|
|
102298
|
+
item: "Item",
|
|
102299
|
+
remove: "Remove",
|
|
102300
|
+
addAnotherStudent: "Add another student",
|
|
102301
|
+
addAnotherItem: "Add another item",
|
|
102302
|
+
back: "Back",
|
|
102303
|
+
"continue": "Continue",
|
|
102304
|
+
submitApplication: "Submit application",
|
|
102305
|
+
requiredAsterisk: "Required fields are marked with an asterisk.",
|
|
102306
|
+
applicationSubmitted: "Application submitted",
|
|
102307
|
+
thankYou: "Thank you. We have received your application and will follow up by email with next steps.",
|
|
102308
|
+
validationSignatureRequired: "Please type your full name to sign.",
|
|
102309
|
+
validationMustBeTrue: "Please check this to continue.",
|
|
102310
|
+
validationRequired: "Please complete this field.",
|
|
102311
|
+
validationInvalid: "Please check this value.",
|
|
102312
|
+
validationMinItems: "Please add at least {min} {noun}.",
|
|
102313
|
+
noApplicationFound: "No application found.",
|
|
102314
|
+
fixErrorsBeforeContinue: "Please fix the highlighted fields to continue.",
|
|
102315
|
+
saving: "Saving…",
|
|
102316
|
+
saveFailed: "Could not save your progress. Please try again.",
|
|
102317
|
+
submitFailed: "Could not submit your application. Please review and try again.",
|
|
102318
|
+
uploading: "Uploading…",
|
|
102319
|
+
uploadFailed: "Could not upload the file. Please try again.",
|
|
102320
|
+
alreadyMemberTitle: "You're already a member",
|
|
102321
|
+
alreadyMember: "Your account is already a member of this program, so a new application isn't needed.",
|
|
102322
|
+
status_draft: "Draft",
|
|
102323
|
+
status_submitted: "Submitted",
|
|
102324
|
+
status_under_review: "Under review",
|
|
102325
|
+
status_needs_more_info: "Needs more info",
|
|
102326
|
+
status_interview_required: "Interview required",
|
|
102327
|
+
status_waitlisted: "Waitlisted",
|
|
102328
|
+
status_approved: "Approved",
|
|
102329
|
+
status_denied: "Denied",
|
|
102330
|
+
status_withdrawn: "Withdrawn",
|
|
102331
|
+
status_pending: "Pending",
|
|
102332
|
+
status_accepted: "Accepted",
|
|
102333
|
+
status_declined: "Declined",
|
|
102334
|
+
modeOpenTitle: "No application needed",
|
|
102335
|
+
modeOpen: "This program accepts members directly — you can join without filling out an application.",
|
|
102336
|
+
modeInviteOnlyTitle: "By invitation only",
|
|
102337
|
+
modeInviteOnly: "This program is invite-only. Please contact the organization for an invitation.",
|
|
102338
|
+
yourApplication: "Your application",
|
|
102339
|
+
submittedOn: "Submitted {date}",
|
|
102340
|
+
approvedNotice: "Your application has been approved. Welcome!",
|
|
102341
|
+
waitlistedNotice: "Your application is on the waitlist. We will notify you when a spot opens.",
|
|
102342
|
+
renewalDeadline: "Renewal deadline: {date}",
|
|
102343
|
+
renewalWindow: "Renewal window: {start} – {end}",
|
|
102344
|
+
renew: "Renew application",
|
|
102345
|
+
renewing: "Preparing renewal…",
|
|
102346
|
+
renewFailed: "Could not start the renewal. Please try again.",
|
|
102347
|
+
withdraw: "Withdraw application",
|
|
102348
|
+
withdrawing: "Withdrawing…",
|
|
102349
|
+
withdrawConfirm: "Withdraw this application?",
|
|
102350
|
+
otherApplications: "Your other applications ({count})",
|
|
102351
|
+
applicationN: "Application #{id}",
|
|
102352
|
+
needsMoreInfoBanner: "The school asked for more information. Update your answers below and resubmit.",
|
|
102353
|
+
saveDraft: "Save draft",
|
|
102354
|
+
savingDraft: "Saving…",
|
|
102355
|
+
draftSaved: "Draft saved",
|
|
102356
|
+
openSubmissionTitle: "You already have an open application",
|
|
102357
|
+
openSubmission: "There is already an open application on this form. Check its status instead of starting a new one.",
|
|
102358
|
+
reapplyCooldownTitle: "Please wait before reapplying",
|
|
102359
|
+
reapplyCooldown: "A recent decision on this form is still cooling down. Please try again later.",
|
|
102360
|
+
renewalWindowBlockedTitle: "Priority renewal window",
|
|
102361
|
+
renewalWindowBlocked: "New applications open after the returning-family priority window. Please come back later.",
|
|
102362
|
+
cannotApplyTitle: "Application unavailable",
|
|
102363
|
+
cannotApply: "An application cannot be started right now. Please contact the organization if you believe this is an error.",
|
|
102364
|
+
notFoundBody: "There is no application form at this address. Check the link, or ask the organization for the right one.",
|
|
102365
|
+
chooseFile: "Choose file…",
|
|
102366
|
+
fileTooLarge: "Each file must be under {max} MB.",
|
|
102367
|
+
maxFileSizeHint: "Max {max} MB"
|
|
102368
|
+
};
|
|
102369
|
+
var profileApplicationsTab$3 = {
|
|
102370
|
+
statusAll: "All",
|
|
102371
|
+
statusSubmitted: "Submitted",
|
|
102372
|
+
statusUnderReview: "Under review",
|
|
102373
|
+
statusNeedsMoreInfo: "Needs info",
|
|
102374
|
+
statusInterviewRequired: "Interview",
|
|
102375
|
+
statusAccepted: "Accepted",
|
|
102376
|
+
statusWaitlisted: "Waitlisted",
|
|
102377
|
+
statusDeclined: "Declined",
|
|
102378
|
+
editQuestions: "Edit Questions",
|
|
102379
|
+
searchPlaceholder: "Search applicants or students",
|
|
102380
|
+
colApplicant: "Applicant",
|
|
102381
|
+
colStudents: "Students",
|
|
102382
|
+
colSubmitted: "Submitted",
|
|
102383
|
+
colStatus: "Status",
|
|
102384
|
+
noMatch: "No applications match your filters.",
|
|
102385
|
+
statusDraft: "Draft",
|
|
102386
|
+
statusApproved: "Approved",
|
|
102387
|
+
statusDenied: "Denied",
|
|
102388
|
+
statusWithdrawn: "Withdrawn",
|
|
102389
|
+
statusPending: "Pending",
|
|
102390
|
+
loadError: "Could not load applications. Try again later.",
|
|
102391
|
+
copyLink: "Copy link",
|
|
102392
|
+
linkCopied: "Copied",
|
|
102393
|
+
formsHeading: "Application forms",
|
|
102394
|
+
formsDescription: "Every application form of this platform. One form is the default applicants land on.",
|
|
102395
|
+
blockedUsers: "Blocked users",
|
|
102396
|
+
blockedUsersDescription: "Families barred from reapplying. Unblocking lets them apply again.",
|
|
102397
|
+
allSubmissions: "All submissions ({count})",
|
|
102398
|
+
newForm: "New form",
|
|
102399
|
+
postApproval_enrolled: "Enrolled",
|
|
102400
|
+
postApproval_enrollment_in_progress: "Enrollment in progress",
|
|
102401
|
+
postApproval_parent_account: "Under parent account",
|
|
102402
|
+
colFormName: "Form",
|
|
102403
|
+
colVersion: "Version",
|
|
102404
|
+
colState: "State",
|
|
102405
|
+
colActions: "Actions",
|
|
102406
|
+
untitledForm: "Untitled form",
|
|
102407
|
+
defaultBadge: "Default",
|
|
102408
|
+
formEnabled: "Enabled",
|
|
102409
|
+
formDisabled: "Disabled",
|
|
102410
|
+
viewSubmissions: "Submissions",
|
|
102411
|
+
editForm: "Edit",
|
|
102412
|
+
noForms: "No application forms yet. Create one to turn on applications.",
|
|
102413
|
+
backToForms: "Back to forms",
|
|
102414
|
+
submissionsFor: "Submissions — {form}",
|
|
102415
|
+
allSubmissionsHeading: "All submissions",
|
|
102416
|
+
candidateFilterPlaceholder: "Student filter, e.g. grade=6",
|
|
102417
|
+
sortLabel: "Sort",
|
|
102418
|
+
sortNewest: "Newest first",
|
|
102419
|
+
sortOldest: "Oldest first",
|
|
102420
|
+
sortRecentlySubmitted: "Recently submitted",
|
|
102421
|
+
sortRecentlyModified: "Recently modified",
|
|
102422
|
+
sortStatus: "By status",
|
|
102423
|
+
backToSubmissions: "Back to submissions",
|
|
102424
|
+
decide: "Decide",
|
|
102425
|
+
decideAll: "Decide for all students",
|
|
102426
|
+
decideFamilyTitle: "Family-wide decision",
|
|
102427
|
+
decideStudentTitle: "Decide — {name}",
|
|
102428
|
+
actionLabel: "Action",
|
|
102429
|
+
actionStartReview: "Start review",
|
|
102430
|
+
actionRequestInfo: "Request more info",
|
|
102431
|
+
actionRequireInterview: "Require interview",
|
|
102432
|
+
actionWaitlist: "Waitlist",
|
|
102433
|
+
actionAccept: "Accept",
|
|
102434
|
+
actionDecline: "Decline",
|
|
102435
|
+
messageLabel: "Message to applicant",
|
|
102436
|
+
messagePlaceholder: "Optional — the form’s template is used when empty.",
|
|
102437
|
+
internalNoteLabel: "Internal note",
|
|
102438
|
+
blockOnDecline: "Also block this family from reapplying",
|
|
102439
|
+
applyAction: "Apply",
|
|
102440
|
+
cancel: "Cancel",
|
|
102441
|
+
transitionApplied: "Decision applied.",
|
|
102442
|
+
illegalTransition: "That action is not allowed from the current status.",
|
|
102443
|
+
noCandidatesYet: "This is still a draft — there is nothing to review yet.",
|
|
102444
|
+
actionFailed: "Something went wrong. Please try again.",
|
|
102445
|
+
submittedAt: "Submitted {date}",
|
|
102446
|
+
studentsHeading: "Students",
|
|
102447
|
+
noStudentsYet: "No student records yet (draft not submitted).",
|
|
102448
|
+
identityLinked: "Account linked",
|
|
102449
|
+
identityInvited: "Invitation sent",
|
|
102450
|
+
identityInviteMissing: "Invitation missing",
|
|
102451
|
+
identityAccountUnlinked: "Account not linked",
|
|
102452
|
+
identityParentAccount: "Parent account",
|
|
102453
|
+
phaseEnrolled: "Enrolled",
|
|
102454
|
+
phaseEnrollmentInProgress: "Enrollment in progress",
|
|
102455
|
+
sendInvite: "Send invite",
|
|
102456
|
+
inviteSent: "Invitation sent.",
|
|
102457
|
+
linkAccount: "Link account",
|
|
102458
|
+
linkAccountTitle: "Link account — {name}",
|
|
102459
|
+
accountEmailLabel: "Account email",
|
|
102460
|
+
accountLinked: "Account linked.",
|
|
102461
|
+
userNotFound: "No account found with that email.",
|
|
102462
|
+
answersHeading: "Answers",
|
|
102463
|
+
entryN: "Entry {n}",
|
|
102464
|
+
maskedValue: "Hidden",
|
|
102465
|
+
sectionMasked: "The \"{section}\" section is hidden — you lack sensitive-data access.",
|
|
102466
|
+
answerYes: "Yes",
|
|
102467
|
+
answerNo: "No",
|
|
102468
|
+
attachmentsHeading: "Attachments",
|
|
102469
|
+
downloadFailed: "Could not download the file.",
|
|
102470
|
+
blockingHeading: "Submit checklist",
|
|
102471
|
+
noBlockingItems: "Nothing is blocking submission.",
|
|
102472
|
+
waive: "Waive",
|
|
102473
|
+
reinstate: "Reinstate",
|
|
102474
|
+
overrideSubmit: "Submit with exceptions",
|
|
102475
|
+
overrideSubmitTitle: "Submit with exceptions?",
|
|
102476
|
+
overrideSubmitBody: "Every missing required item will be waived and the application submitted on the family’s behalf.",
|
|
102477
|
+
overrideSubmitted: "Application submitted with exceptions.",
|
|
102478
|
+
placementsHeading: "Placement tests",
|
|
102479
|
+
assignPlacement: "Assign placement",
|
|
102480
|
+
noPlacements: "No placement tests assigned.",
|
|
102481
|
+
placementResult: "Result",
|
|
102482
|
+
placementStatusLabel: "Placement status",
|
|
102483
|
+
placementUpdated: "Placement updated.",
|
|
102484
|
+
placementAssigned: "Placement assigned.",
|
|
102485
|
+
placementAgentLabel: "Placement agent",
|
|
102486
|
+
selectAgentPlaceholder: "Select an agent...",
|
|
102487
|
+
searchAgentsPlaceholder: "Search agents...",
|
|
102488
|
+
loadingAgents: "Loading agents...",
|
|
102489
|
+
noAgentsFound: "No agents found",
|
|
102490
|
+
clearAgent: "Remove {agent}",
|
|
102491
|
+
placementSubjectLabel: "Subject",
|
|
102492
|
+
studentLabel: "Student",
|
|
102493
|
+
placement_not_assigned: "Not assigned",
|
|
102494
|
+
placement_assigned: "Assigned",
|
|
102495
|
+
placement_started: "Started",
|
|
102496
|
+
placement_completed: "Completed",
|
|
102497
|
+
placement_reviewed: "Reviewed",
|
|
102498
|
+
placement_confirmed: "Confirmed",
|
|
102499
|
+
assignmentsHeading: "Course assignments",
|
|
102500
|
+
assignCourse: "Assign course",
|
|
102501
|
+
noAssignments: "No courses assigned.",
|
|
102502
|
+
startDateLabel: "Start date",
|
|
102503
|
+
endDateLabel: "End date",
|
|
102504
|
+
courseAssigned: "Course assigned.",
|
|
102505
|
+
assignmentRemoved: "Assignment removed.",
|
|
102506
|
+
removeAssignment: "Remove assignment",
|
|
102507
|
+
alreadyAssigned: "That student already has an active assignment for this course.",
|
|
102508
|
+
coursePlatformMismatch: "That course does not belong to this platform.",
|
|
102509
|
+
notesHeading: "Internal notes",
|
|
102510
|
+
addNote: "Add note",
|
|
102511
|
+
addNotePlaceholder: "Write an internal note… (never shown to applicants)",
|
|
102512
|
+
saveNote: "Save",
|
|
102513
|
+
editNote: "Edit note",
|
|
102514
|
+
deleteNote: "Delete note",
|
|
102515
|
+
noteKindLabel: "Note type",
|
|
102516
|
+
noteKindGeneral: "General",
|
|
102517
|
+
noteKindInterview: "Interview",
|
|
102518
|
+
auditHeading: "History",
|
|
102519
|
+
noEvents: "No events yet.",
|
|
102520
|
+
familyWide: "Family",
|
|
102521
|
+
colUser: "User",
|
|
102522
|
+
colReason: "Reason",
|
|
102523
|
+
colBlockedBy: "Blocked by",
|
|
102524
|
+
noBlockedUsers: "No blocked users.",
|
|
102525
|
+
unblock: "Unblock",
|
|
102526
|
+
unblocked: "User unblocked.",
|
|
102527
|
+
copyFormLink: "Copy application link for {form}",
|
|
102528
|
+
coursesLabel: "Courses",
|
|
102529
|
+
removeCourse: "Remove {course}"
|
|
102530
|
+
};
|
|
102531
|
+
var profileApplicationsQuestionsEditor$3 = {
|
|
102532
|
+
backToApplications: "Back to applications",
|
|
102533
|
+
saveChanges: "Save changes",
|
|
102534
|
+
savedToast: "Application form saved",
|
|
102535
|
+
editApplicationQuestions: "Edit application questions",
|
|
102536
|
+
repeatable: "Repeatable",
|
|
102537
|
+
editQuestion: "Edit question",
|
|
102538
|
+
removeQuestion: "Remove question",
|
|
102539
|
+
required: "Required",
|
|
102540
|
+
addQuestion: "Add question",
|
|
102541
|
+
addSection: "Add section",
|
|
102542
|
+
saving: "Saving…",
|
|
102543
|
+
saveFailedToast: "Could not save the application form",
|
|
102544
|
+
editorDescription: "Add, reorder, or remove the questions applicants answer when they apply.",
|
|
102545
|
+
unsavedChanges: "Unsaved changes",
|
|
102546
|
+
selectFormLabel: "Form",
|
|
102547
|
+
untitledForm: "Untitled form",
|
|
102548
|
+
defaultBadge: "Default",
|
|
102549
|
+
makeDefault: "Make default form",
|
|
102550
|
+
enabledLabel: "Accepting applications",
|
|
102551
|
+
versionLabel: "Version {version}",
|
|
102552
|
+
formTitleLabel: "Form title",
|
|
102553
|
+
formDescriptionLabel: "Description",
|
|
102554
|
+
sectionTitlePlaceholder: "Section title",
|
|
102555
|
+
candidateBadge: "Students",
|
|
102556
|
+
candidateToggle: "Students section",
|
|
102557
|
+
moveUp: "Move up",
|
|
102558
|
+
moveDown: "Move down",
|
|
102559
|
+
removeSection: "Remove section",
|
|
102560
|
+
emptySection: "No questions yet.",
|
|
102561
|
+
noSections: "This form has no sections yet. Add one to start collecting answers.",
|
|
102562
|
+
newSectionTitle: "New section",
|
|
102563
|
+
fieldLabelLabel: "Label",
|
|
102564
|
+
fieldTypeLabel: "Type",
|
|
102565
|
+
fieldPlaceholderLabel: "Placeholder",
|
|
102566
|
+
fieldHelpTextLabel: "Help text",
|
|
102567
|
+
fieldOptionsLabel: "Options (one per line, value | label)",
|
|
102568
|
+
fieldContentLabel: "Content (markdown)",
|
|
102569
|
+
fieldRequiredLabel: "Required",
|
|
102570
|
+
fieldMustBeTrueLabel: "Must be checked to submit",
|
|
102571
|
+
fieldKeyNote: "Answer key: {key}",
|
|
102572
|
+
cancel: "Cancel",
|
|
102573
|
+
saveQuestion: "Save question",
|
|
102574
|
+
fixValidation: "Fix these issues to save:",
|
|
102575
|
+
loadError: "Could not load application forms. Try again later.",
|
|
102576
|
+
confirmRemoveSection: "Remove this section and all its questions?",
|
|
102577
|
+
confirmRemoveQuestion: "Remove this question?",
|
|
102578
|
+
confirmRemove: "Remove",
|
|
102579
|
+
collapseSection: "Collapse section",
|
|
102580
|
+
expandSection: "Expand section"
|
|
102280
102581
|
};
|
|
102281
102582
|
var en = {
|
|
102282
102583
|
accessTabAddAccess: accessTabAddAccess$3,
|
|
@@ -102524,7 +102825,11 @@ var en = {
|
|
|
102524
102825
|
benchmarksDeleteItemConfirm: benchmarksDeleteItemConfirm$3,
|
|
102525
102826
|
benchmarksAddItemsModal: benchmarksAddItemsModal$3,
|
|
102526
102827
|
platformSidebar: platformSidebar$3,
|
|
102527
|
-
platformNavbar: platformNavbar$3
|
|
102828
|
+
platformNavbar: platformNavbar$3,
|
|
102829
|
+
placementExam: placementExam$3,
|
|
102830
|
+
applyApplyWizard: applyApplyWizard$3,
|
|
102831
|
+
profileApplicationsTab: profileApplicationsTab$3,
|
|
102832
|
+
profileApplicationsQuestionsEditor: profileApplicationsQuestionsEditor$3
|
|
102528
102833
|
};
|
|
102529
102834
|
|
|
102530
102835
|
var accessTabAddAccess$2 = {
|
|
@@ -105720,7 +106025,9 @@ var profileAdmin$2 = {
|
|
|
105720
106025
|
rolesDescription: "Un rôle RBAC est un ensemble nommé de permissions (actions appliquées à des ressources dans des politiques).",
|
|
105721
106026
|
policiesDescription: "Une politique RBAC accorde les permissions d'un rôle à un ensemble d'utilisateurs ou de groupes sur un ensemble de ressources. Les ressources sont hiérarchiques : accorder sur /platforms/{pk}/mentors/ couvre tous les agents, tandis que /platforms/{pk}/mentors/{pk}/ se limite à un seul.",
|
|
105722
106027
|
teamsDescription: "Une équipe est un ensemble organisé d'utilisateurs que vous pouvez partager avec d'autres — par exemple, accorder à un autre utilisateur la permission de consulter les analyses de l'équipe ou de lui envoyer des notifications.",
|
|
105723
|
-
alertsDescription: "Une alerte associe une équipe d'utilisateurs surveillés à un ou plusieurs observateurs qui sont notifiés lorsque des événements surveillés se produisent pour un utilisateur observé. Chaque observateur s'abonne à son propre sous-ensemble d'événements."
|
|
106028
|
+
alertsDescription: "Une alerte associe une équipe d'utilisateurs surveillés à un ou plusieurs observateurs qui sont notifiés lorsque des événements surveillés se produisent pour un utilisateur observé. Chaque observateur s'abonne à son propre sous-ensemble d'événements.",
|
|
106029
|
+
applicationsTab: "Candidatures",
|
|
106030
|
+
applicationsTabDescription: "Consultez les candidatures à votre organisation et modifiez les questions auxquelles les candidats répondent lorsqu'ils postulent."
|
|
105724
106031
|
};
|
|
105725
106032
|
var profileAutoRechargeModal$2 = {
|
|
105726
106033
|
title: "Gérer l'utilisation",
|
|
@@ -107976,7 +108283,306 @@ var platformNavbar$2 = {
|
|
|
107976
108283
|
search: "Rechercher",
|
|
107977
108284
|
searchPlaceholder: "Rechercher",
|
|
107978
108285
|
logIn: "Se connecter",
|
|
107979
|
-
signUpForFree: "S'inscrire gratuitement"
|
|
108286
|
+
signUpForFree: "S'inscrire gratuitement",
|
|
108287
|
+
placementExamsAriaLabel: "Tests de positionnement ({count} disponibles)",
|
|
108288
|
+
placementExamsHeader: "Tests de positionnement",
|
|
108289
|
+
placementExamFallback: "Test de positionnement"
|
|
108290
|
+
};
|
|
108291
|
+
var placementExam$2 = {
|
|
108292
|
+
title: "Test de positionnement",
|
|
108293
|
+
back: "Retour"
|
|
108294
|
+
};
|
|
108295
|
+
var applyApplyWizard$2 = {
|
|
108296
|
+
application: "Candidature",
|
|
108297
|
+
selectAnOption: "Sélectionnez une option",
|
|
108298
|
+
stepProgress: "Étape {current} sur {total}",
|
|
108299
|
+
reviewAndSubmit: "Vérifier et envoyer",
|
|
108300
|
+
checkYourAnswers: "Vérifiez vos réponses, puis envoyez votre candidature.",
|
|
108301
|
+
bySubmitting: "En envoyant, vous confirmez que les informations fournies sont exactes.",
|
|
108302
|
+
student: "Élève",
|
|
108303
|
+
item: "Élément",
|
|
108304
|
+
remove: "Supprimer",
|
|
108305
|
+
addAnotherStudent: "Ajouter un autre élève",
|
|
108306
|
+
addAnotherItem: "Ajouter un autre élément",
|
|
108307
|
+
back: "Retour",
|
|
108308
|
+
"continue": "Continuer",
|
|
108309
|
+
submitApplication: "Envoyer la candidature",
|
|
108310
|
+
requiredAsterisk: "Les champs obligatoires sont marqués d'un astérisque.",
|
|
108311
|
+
applicationSubmitted: "Candidature envoyée",
|
|
108312
|
+
thankYou: "Merci. Nous avons bien reçu votre candidature et vous contacterons par e-mail pour la suite.",
|
|
108313
|
+
validationSignatureRequired: "Veuillez saisir votre nom complet pour signer.",
|
|
108314
|
+
validationMustBeTrue: "Veuillez cocher cette case pour continuer.",
|
|
108315
|
+
validationRequired: "Veuillez remplir ce champ.",
|
|
108316
|
+
validationInvalid: "Veuillez vérifier cette valeur.",
|
|
108317
|
+
validationMinItems: "Veuillez ajouter au moins {min} {noun}.",
|
|
108318
|
+
noApplicationFound: "Aucune candidature trouvée.",
|
|
108319
|
+
fixErrorsBeforeContinue: "Corrigez les champs mis en évidence pour continuer.",
|
|
108320
|
+
saving: "Enregistrement…",
|
|
108321
|
+
saveFailed: "Impossible d'enregistrer votre progression. Veuillez réessayer.",
|
|
108322
|
+
submitFailed: "Impossible d'envoyer votre candidature. Vérifiez-la et réessayez.",
|
|
108323
|
+
uploading: "Téléversement…",
|
|
108324
|
+
uploadFailed: "Impossible de téléverser le fichier. Veuillez réessayer.",
|
|
108325
|
+
alreadyMemberTitle: "Vous êtes déjà membre",
|
|
108326
|
+
alreadyMember: "Votre compte est déjà membre de ce programme ; aucune nouvelle candidature n'est nécessaire.",
|
|
108327
|
+
status_draft: "Brouillon",
|
|
108328
|
+
status_submitted: "Envoyée",
|
|
108329
|
+
status_under_review: "En cours d’examen",
|
|
108330
|
+
status_needs_more_info: "Informations requises",
|
|
108331
|
+
status_interview_required: "Entretien requis",
|
|
108332
|
+
status_waitlisted: "Liste d’attente",
|
|
108333
|
+
status_approved: "Approuvée",
|
|
108334
|
+
status_denied: "Refusée",
|
|
108335
|
+
status_withdrawn: "Retirée",
|
|
108336
|
+
status_pending: "En attente",
|
|
108337
|
+
status_accepted: "Accepté",
|
|
108338
|
+
status_declined: "Refusé",
|
|
108339
|
+
modeOpenTitle: "Aucune candidature requise",
|
|
108340
|
+
modeOpen: "Ce programme accepte les membres directement — vous pouvez le rejoindre sans candidature.",
|
|
108341
|
+
modeInviteOnlyTitle: "Sur invitation uniquement",
|
|
108342
|
+
modeInviteOnly: "Ce programme est accessible sur invitation uniquement. Contactez l’organisation pour en recevoir une.",
|
|
108343
|
+
yourApplication: "Votre candidature",
|
|
108344
|
+
submittedOn: "Envoyée le {date}",
|
|
108345
|
+
approvedNotice: "Votre candidature a été approuvée. Bienvenue !",
|
|
108346
|
+
waitlistedNotice: "Votre candidature est en liste d’attente. Nous vous préviendrons dès qu’une place se libère.",
|
|
108347
|
+
renewalDeadline: "Date limite de renouvellement : {date}",
|
|
108348
|
+
renewalWindow: "Période de renouvellement : {start} – {end}",
|
|
108349
|
+
renew: "Renouveler la candidature",
|
|
108350
|
+
renewing: "Préparation du renouvellement…",
|
|
108351
|
+
renewFailed: "Impossible de démarrer le renouvellement. Veuillez réessayer.",
|
|
108352
|
+
withdraw: "Retirer la candidature",
|
|
108353
|
+
withdrawing: "Retrait…",
|
|
108354
|
+
withdrawConfirm: "Retirer cette candidature ?",
|
|
108355
|
+
otherApplications: "Vos autres candidatures ({count})",
|
|
108356
|
+
applicationN: "Candidature n° {id}",
|
|
108357
|
+
needsMoreInfoBanner: "L’école a demandé des informations complémentaires. Mettez à jour vos réponses et renvoyez.",
|
|
108358
|
+
saveDraft: "Enregistrer le brouillon",
|
|
108359
|
+
savingDraft: "Enregistrement…",
|
|
108360
|
+
draftSaved: "Brouillon enregistré",
|
|
108361
|
+
openSubmissionTitle: "Vous avez déjà une candidature en cours",
|
|
108362
|
+
openSubmission: "Une candidature est déjà en cours sur ce formulaire. Consultez son statut au lieu d’en commencer une nouvelle.",
|
|
108363
|
+
reapplyCooldownTitle: "Veuillez patienter avant de repostuler",
|
|
108364
|
+
reapplyCooldown: "Une décision récente sur ce formulaire est encore en période d’attente. Réessayez plus tard.",
|
|
108365
|
+
renewalWindowBlockedTitle: "Période de renouvellement prioritaire",
|
|
108366
|
+
renewalWindowBlocked: "Les nouvelles candidatures ouvrent après la période prioritaire des familles de retour. Revenez plus tard.",
|
|
108367
|
+
cannotApplyTitle: "Candidature indisponible",
|
|
108368
|
+
cannotApply: "Impossible de démarrer une candidature pour le moment. Contactez l’organisation si vous pensez qu’il s’agit d’une erreur.",
|
|
108369
|
+
notFoundBody: "Aucun formulaire de candidature à cette adresse. Vérifiez le lien ou demandez le bon à l’organisation.",
|
|
108370
|
+
chooseFile: "Choisir un fichier…",
|
|
108371
|
+
fileTooLarge: "Chaque fichier doit faire moins de {max} Mo.",
|
|
108372
|
+
maxFileSizeHint: "Max {max} Mo"
|
|
108373
|
+
};
|
|
108374
|
+
var profileApplicationsTab$2 = {
|
|
108375
|
+
statusAll: "Toutes",
|
|
108376
|
+
statusSubmitted: "Envoyées",
|
|
108377
|
+
statusUnderReview: "En cours d'examen",
|
|
108378
|
+
statusNeedsMoreInfo: "Infos requises",
|
|
108379
|
+
statusInterviewRequired: "Entretien",
|
|
108380
|
+
statusAccepted: "Acceptées",
|
|
108381
|
+
statusWaitlisted: "Liste d'attente",
|
|
108382
|
+
statusDeclined: "Refusées",
|
|
108383
|
+
editQuestions: "Modifier les questions",
|
|
108384
|
+
searchPlaceholder: "Rechercher des candidats ou des élèves",
|
|
108385
|
+
colApplicant: "Candidat",
|
|
108386
|
+
colStudents: "Élèves",
|
|
108387
|
+
colSubmitted: "Envoyée le",
|
|
108388
|
+
colStatus: "Statut",
|
|
108389
|
+
noMatch: "Aucune candidature ne correspond à vos filtres.",
|
|
108390
|
+
statusDraft: "Brouillon",
|
|
108391
|
+
statusApproved: "Approuvées",
|
|
108392
|
+
statusDenied: "Refusées",
|
|
108393
|
+
statusWithdrawn: "Retirées",
|
|
108394
|
+
statusPending: "En attente",
|
|
108395
|
+
loadError: "Impossible de charger les candidatures. Réessayez plus tard.",
|
|
108396
|
+
copyLink: "Copier le lien",
|
|
108397
|
+
linkCopied: "Copié",
|
|
108398
|
+
formsHeading: "Formulaires de candidature",
|
|
108399
|
+
formsDescription: "Tous les formulaires de candidature de cette plateforme. L’un d’eux est celui par défaut.",
|
|
108400
|
+
blockedUsers: "Utilisateurs bloqués",
|
|
108401
|
+
blockedUsersDescription: "Familles empêchées de repostuler. Le déblocage leur permet de candidater à nouveau.",
|
|
108402
|
+
allSubmissions: "Toutes les candidatures ({count})",
|
|
108403
|
+
newForm: "Nouveau formulaire",
|
|
108404
|
+
postApproval_enrolled: "Inscrits",
|
|
108405
|
+
postApproval_enrollment_in_progress: "Inscription en cours",
|
|
108406
|
+
postApproval_parent_account: "Sous le compte du parent",
|
|
108407
|
+
colFormName: "Formulaire",
|
|
108408
|
+
colVersion: "Version",
|
|
108409
|
+
colState: "État",
|
|
108410
|
+
colActions: "Actions",
|
|
108411
|
+
untitledForm: "Formulaire sans titre",
|
|
108412
|
+
defaultBadge: "Par défaut",
|
|
108413
|
+
formEnabled: "Activé",
|
|
108414
|
+
formDisabled: "Désactivé",
|
|
108415
|
+
viewSubmissions: "Candidatures",
|
|
108416
|
+
editForm: "Modifier",
|
|
108417
|
+
noForms: "Aucun formulaire de candidature. Créez-en un pour activer les candidatures.",
|
|
108418
|
+
backToForms: "Retour aux formulaires",
|
|
108419
|
+
submissionsFor: "Candidatures — {form}",
|
|
108420
|
+
allSubmissionsHeading: "Toutes les candidatures",
|
|
108421
|
+
candidateFilterPlaceholder: "Filtre élève, ex. grade=6",
|
|
108422
|
+
sortLabel: "Trier",
|
|
108423
|
+
sortNewest: "Plus récentes d’abord",
|
|
108424
|
+
sortOldest: "Plus anciennes d’abord",
|
|
108425
|
+
sortRecentlySubmitted: "Récemment envoyées",
|
|
108426
|
+
sortRecentlyModified: "Récemment modifiées",
|
|
108427
|
+
sortStatus: "Par statut",
|
|
108428
|
+
backToSubmissions: "Retour aux candidatures",
|
|
108429
|
+
decide: "Décider",
|
|
108430
|
+
decideAll: "Décider pour tous les élèves",
|
|
108431
|
+
decideFamilyTitle: "Décision pour toute la famille",
|
|
108432
|
+
decideStudentTitle: "Décider — {name}",
|
|
108433
|
+
actionLabel: "Action",
|
|
108434
|
+
actionStartReview: "Commencer l’examen",
|
|
108435
|
+
actionRequestInfo: "Demander des informations",
|
|
108436
|
+
actionRequireInterview: "Exiger un entretien",
|
|
108437
|
+
actionWaitlist: "Liste d’attente",
|
|
108438
|
+
actionAccept: "Accepter",
|
|
108439
|
+
actionDecline: "Refuser",
|
|
108440
|
+
messageLabel: "Message au candidat",
|
|
108441
|
+
messagePlaceholder: "Facultatif — le modèle du formulaire est utilisé si vide.",
|
|
108442
|
+
internalNoteLabel: "Note interne",
|
|
108443
|
+
blockOnDecline: "Empêcher aussi cette famille de repostuler",
|
|
108444
|
+
applyAction: "Appliquer",
|
|
108445
|
+
cancel: "Annuler",
|
|
108446
|
+
transitionApplied: "Décision appliquée.",
|
|
108447
|
+
illegalTransition: "Cette action n’est pas autorisée depuis le statut actuel.",
|
|
108448
|
+
noCandidatesYet: "C’est encore un brouillon — rien à examiner pour l’instant.",
|
|
108449
|
+
actionFailed: "Une erreur est survenue. Veuillez réessayer.",
|
|
108450
|
+
submittedAt: "Envoyée le {date}",
|
|
108451
|
+
studentsHeading: "Élèves",
|
|
108452
|
+
noStudentsYet: "Pas encore de dossiers élèves (brouillon non envoyé).",
|
|
108453
|
+
identityLinked: "Compte lié",
|
|
108454
|
+
identityInvited: "Invitation envoyée",
|
|
108455
|
+
identityInviteMissing: "Invitation manquante",
|
|
108456
|
+
identityAccountUnlinked: "Compte non lié",
|
|
108457
|
+
identityParentAccount: "Compte du parent",
|
|
108458
|
+
phaseEnrolled: "Inscrit",
|
|
108459
|
+
phaseEnrollmentInProgress: "Inscription en cours",
|
|
108460
|
+
sendInvite: "Envoyer l’invitation",
|
|
108461
|
+
inviteSent: "Invitation envoyée.",
|
|
108462
|
+
linkAccount: "Lier le compte",
|
|
108463
|
+
linkAccountTitle: "Lier le compte — {name}",
|
|
108464
|
+
accountEmailLabel: "E-mail du compte",
|
|
108465
|
+
accountLinked: "Compte lié.",
|
|
108466
|
+
userNotFound: "Aucun compte trouvé avec cet e-mail.",
|
|
108467
|
+
answersHeading: "Réponses",
|
|
108468
|
+
entryN: "Entrée {n}",
|
|
108469
|
+
maskedValue: "Masqué",
|
|
108470
|
+
sectionMasked: "La section « {section} » est masquée — vous n’avez pas accès aux données sensibles.",
|
|
108471
|
+
answerYes: "Oui",
|
|
108472
|
+
answerNo: "Non",
|
|
108473
|
+
attachmentsHeading: "Pièces jointes",
|
|
108474
|
+
downloadFailed: "Impossible de télécharger le fichier.",
|
|
108475
|
+
blockingHeading: "Liste de blocage",
|
|
108476
|
+
noBlockingItems: "Rien ne bloque l’envoi.",
|
|
108477
|
+
waive: "Dispenser",
|
|
108478
|
+
reinstate: "Rétablir",
|
|
108479
|
+
overrideSubmit: "Envoyer avec exceptions",
|
|
108480
|
+
overrideSubmitTitle: "Envoyer avec exceptions ?",
|
|
108481
|
+
overrideSubmitBody: "Chaque élément requis manquant sera dispensé et la candidature envoyée au nom de la famille.",
|
|
108482
|
+
overrideSubmitted: "Candidature envoyée avec exceptions.",
|
|
108483
|
+
placementsHeading: "Tests de niveau",
|
|
108484
|
+
assignPlacement: "Assigner un test",
|
|
108485
|
+
noPlacements: "Aucun test de niveau assigné.",
|
|
108486
|
+
placementResult: "Résultat",
|
|
108487
|
+
placementStatusLabel: "Statut du test",
|
|
108488
|
+
placementUpdated: "Test mis à jour.",
|
|
108489
|
+
placementAssigned: "Test assigné.",
|
|
108490
|
+
placementAgentLabel: "Agent de positionnement",
|
|
108491
|
+
selectAgentPlaceholder: "Sélectionner un agent...",
|
|
108492
|
+
searchAgentsPlaceholder: "Rechercher des agents...",
|
|
108493
|
+
loadingAgents: "Chargement des agents...",
|
|
108494
|
+
noAgentsFound: "Aucun agent trouvé",
|
|
108495
|
+
clearAgent: "Retirer {agent}",
|
|
108496
|
+
placementSubjectLabel: "Matière",
|
|
108497
|
+
studentLabel: "Élève",
|
|
108498
|
+
placement_not_assigned: "Non assigné",
|
|
108499
|
+
placement_assigned: "Assigné",
|
|
108500
|
+
placement_started: "Commencé",
|
|
108501
|
+
placement_completed: "Terminé",
|
|
108502
|
+
placement_reviewed: "Examiné",
|
|
108503
|
+
placement_confirmed: "Confirmé",
|
|
108504
|
+
assignmentsHeading: "Affectations de cours",
|
|
108505
|
+
assignCourse: "Affecter un cours",
|
|
108506
|
+
noAssignments: "Aucun cours affecté.",
|
|
108507
|
+
startDateLabel: "Date de début",
|
|
108508
|
+
endDateLabel: "Date de fin",
|
|
108509
|
+
courseAssigned: "Cours affecté.",
|
|
108510
|
+
assignmentRemoved: "Affectation retirée.",
|
|
108511
|
+
removeAssignment: "Retirer l’affectation",
|
|
108512
|
+
alreadyAssigned: "Cet élève a déjà une affectation active pour ce cours.",
|
|
108513
|
+
coursePlatformMismatch: "Ce cours n’appartient pas à cette plateforme.",
|
|
108514
|
+
notesHeading: "Notes internes",
|
|
108515
|
+
addNote: "Ajouter une note",
|
|
108516
|
+
addNotePlaceholder: "Rédigez une note interne… (jamais visible par les candidats)",
|
|
108517
|
+
saveNote: "Enregistrer",
|
|
108518
|
+
editNote: "Modifier la note",
|
|
108519
|
+
deleteNote: "Supprimer la note",
|
|
108520
|
+
noteKindLabel: "Type de note",
|
|
108521
|
+
noteKindGeneral: "Générale",
|
|
108522
|
+
noteKindInterview: "Entretien",
|
|
108523
|
+
auditHeading: "Historique",
|
|
108524
|
+
noEvents: "Aucun événement.",
|
|
108525
|
+
familyWide: "Famille",
|
|
108526
|
+
colUser: "Utilisateur",
|
|
108527
|
+
colReason: "Motif",
|
|
108528
|
+
colBlockedBy: "Bloqué par",
|
|
108529
|
+
noBlockedUsers: "Aucun utilisateur bloqué.",
|
|
108530
|
+
unblock: "Débloquer",
|
|
108531
|
+
unblocked: "Utilisateur débloqué.",
|
|
108532
|
+
copyFormLink: "Copier le lien de candidature de {form}",
|
|
108533
|
+
coursesLabel: "Cours",
|
|
108534
|
+
removeCourse: "Retirer {course}"
|
|
108535
|
+
};
|
|
108536
|
+
var profileApplicationsQuestionsEditor$2 = {
|
|
108537
|
+
backToApplications: "Retour aux candidatures",
|
|
108538
|
+
saveChanges: "Enregistrer les modifications",
|
|
108539
|
+
savedToast: "Formulaire de candidature enregistré",
|
|
108540
|
+
editApplicationQuestions: "Modifier les questions de candidature",
|
|
108541
|
+
repeatable: "Répétable",
|
|
108542
|
+
editQuestion: "Modifier la question",
|
|
108543
|
+
removeQuestion: "Supprimer la question",
|
|
108544
|
+
required: "Obligatoire",
|
|
108545
|
+
addQuestion: "Ajouter une question",
|
|
108546
|
+
addSection: "Ajouter une section",
|
|
108547
|
+
saving: "Enregistrement…",
|
|
108548
|
+
saveFailedToast: "Impossible d'enregistrer le formulaire de candidature",
|
|
108549
|
+
editorDescription: "Ajoutez, réorganisez ou supprimez les questions auxquelles les candidats répondent lorsqu'ils postulent.",
|
|
108550
|
+
unsavedChanges: "Modifications non enregistrées",
|
|
108551
|
+
selectFormLabel: "Formulaire",
|
|
108552
|
+
untitledForm: "Formulaire sans titre",
|
|
108553
|
+
defaultBadge: "Par défaut",
|
|
108554
|
+
makeDefault: "Définir comme formulaire par défaut",
|
|
108555
|
+
enabledLabel: "Candidatures ouvertes",
|
|
108556
|
+
versionLabel: "Version {version}",
|
|
108557
|
+
formTitleLabel: "Titre du formulaire",
|
|
108558
|
+
formDescriptionLabel: "Description",
|
|
108559
|
+
sectionTitlePlaceholder: "Titre de la section",
|
|
108560
|
+
candidateBadge: "Élèves",
|
|
108561
|
+
candidateToggle: "Section des élèves",
|
|
108562
|
+
moveUp: "Monter",
|
|
108563
|
+
moveDown: "Descendre",
|
|
108564
|
+
removeSection: "Supprimer la section",
|
|
108565
|
+
emptySection: "Aucune question pour le moment.",
|
|
108566
|
+
noSections: "Ce formulaire n'a pas encore de sections. Ajoutez-en une pour commencer à recueillir des réponses.",
|
|
108567
|
+
newSectionTitle: "Nouvelle section",
|
|
108568
|
+
fieldLabelLabel: "Libellé",
|
|
108569
|
+
fieldTypeLabel: "Type",
|
|
108570
|
+
fieldPlaceholderLabel: "Texte indicatif",
|
|
108571
|
+
fieldHelpTextLabel: "Texte d'aide",
|
|
108572
|
+
fieldOptionsLabel: "Options (une par ligne, valeur | libellé)",
|
|
108573
|
+
fieldContentLabel: "Contenu (markdown)",
|
|
108574
|
+
fieldRequiredLabel: "Obligatoire",
|
|
108575
|
+
fieldMustBeTrueLabel: "Doit être cochée pour envoyer",
|
|
108576
|
+
fieldKeyNote: "Clé de réponse : {key}",
|
|
108577
|
+
cancel: "Annuler",
|
|
108578
|
+
saveQuestion: "Enregistrer la question",
|
|
108579
|
+
fixValidation: "Corrigez ces problèmes pour enregistrer :",
|
|
108580
|
+
loadError: "Impossible de charger les formulaires de candidature. Réessayez plus tard.",
|
|
108581
|
+
confirmRemoveSection: "Supprimer cette section et toutes ses questions ?",
|
|
108582
|
+
confirmRemoveQuestion: "Supprimer cette question ?",
|
|
108583
|
+
confirmRemove: "Supprimer",
|
|
108584
|
+
collapseSection: "Réduire la section",
|
|
108585
|
+
expandSection: "Développer la section"
|
|
107980
108586
|
};
|
|
107981
108587
|
var fr = {
|
|
107982
108588
|
accessTabAddAccess: accessTabAddAccess$2,
|
|
@@ -108224,7 +108830,11 @@ var fr = {
|
|
|
108224
108830
|
benchmarksDeleteItemConfirm: benchmarksDeleteItemConfirm$2,
|
|
108225
108831
|
benchmarksAddItemsModal: benchmarksAddItemsModal$2,
|
|
108226
108832
|
platformSidebar: platformSidebar$2,
|
|
108227
|
-
platformNavbar: platformNavbar$2
|
|
108833
|
+
platformNavbar: platformNavbar$2,
|
|
108834
|
+
placementExam: placementExam$2,
|
|
108835
|
+
applyApplyWizard: applyApplyWizard$2,
|
|
108836
|
+
profileApplicationsTab: profileApplicationsTab$2,
|
|
108837
|
+
profileApplicationsQuestionsEditor: profileApplicationsQuestionsEditor$2
|
|
108228
108838
|
};
|
|
108229
108839
|
|
|
108230
108840
|
var accessTabAddAccess$1 = {
|
|
@@ -111420,7 +112030,9 @@ var profileAdmin$1 = {
|
|
|
111420
112030
|
rolesDescription: "Un rol RBAC es un conjunto nombrado de permisos (acciones aplicadas a recursos en políticas).",
|
|
111421
112031
|
policiesDescription: "Una política RBAC otorga los permisos de un rol a un conjunto de usuarios o grupos sobre un conjunto de recursos. Los recursos son jerárquicos: otorgar en /platforms/{pk}/mentors/ cubre todos los agentes, mientras que /platforms/{pk}/mentors/{pk}/ limita a uno solo.",
|
|
111422
112032
|
teamsDescription: "Un equipo es un conjunto organizado de usuarios que puedes compartir con otros — por ejemplo, otorgar a otro usuario permiso para ver las analíticas del equipo o enviarles notificaciones.",
|
|
111423
|
-
alertsDescription: "Una alerta empareja un equipo de usuarios vigilados con uno o más observadores que reciben notificaciones cuando ocurren eventos monitoreados para un usuario vigilado. Cada observador se suscribe a su propio subconjunto de eventos."
|
|
112033
|
+
alertsDescription: "Una alerta empareja un equipo de usuarios vigilados con uno o más observadores que reciben notificaciones cuando ocurren eventos monitoreados para un usuario vigilado. Cada observador se suscribe a su propio subconjunto de eventos.",
|
|
112034
|
+
applicationsTab: "Solicitudes",
|
|
112035
|
+
applicationsTabDescription: "Revise las solicitudes a su organización y edite las preguntas que responden los solicitantes cuando postulan."
|
|
111424
112036
|
};
|
|
111425
112037
|
var profileAutoRechargeModal$1 = {
|
|
111426
112038
|
title: "Gestionar uso",
|
|
@@ -113676,7 +114288,306 @@ var platformNavbar$1 = {
|
|
|
113676
114288
|
search: "Buscar",
|
|
113677
114289
|
searchPlaceholder: "Buscar",
|
|
113678
114290
|
logIn: "Iniciar sesión",
|
|
113679
|
-
signUpForFree: "Registrarse gratis"
|
|
114291
|
+
signUpForFree: "Registrarse gratis",
|
|
114292
|
+
placementExamsAriaLabel: "Exámenes de nivelación ({count} disponibles)",
|
|
114293
|
+
placementExamsHeader: "Exámenes de nivelación",
|
|
114294
|
+
placementExamFallback: "Examen de nivelación"
|
|
114295
|
+
};
|
|
114296
|
+
var placementExam$1 = {
|
|
114297
|
+
title: "Examen de nivelación",
|
|
114298
|
+
back: "Volver"
|
|
114299
|
+
};
|
|
114300
|
+
var applyApplyWizard$1 = {
|
|
114301
|
+
application: "Solicitud",
|
|
114302
|
+
selectAnOption: "Seleccione una opción",
|
|
114303
|
+
stepProgress: "Paso {current} de {total}",
|
|
114304
|
+
reviewAndSubmit: "Revisar y enviar",
|
|
114305
|
+
checkYourAnswers: "Revise sus respuestas y luego envíe su solicitud.",
|
|
114306
|
+
bySubmitting: "Al enviar, confirma que la información proporcionada es correcta.",
|
|
114307
|
+
student: "Estudiante",
|
|
114308
|
+
item: "Elemento",
|
|
114309
|
+
remove: "Eliminar",
|
|
114310
|
+
addAnotherStudent: "Agregar otro estudiante",
|
|
114311
|
+
addAnotherItem: "Agregar otro elemento",
|
|
114312
|
+
back: "Atrás",
|
|
114313
|
+
"continue": "Continuar",
|
|
114314
|
+
submitApplication: "Enviar solicitud",
|
|
114315
|
+
requiredAsterisk: "Los campos obligatorios están marcados con un asterisco.",
|
|
114316
|
+
applicationSubmitted: "Solicitud enviada",
|
|
114317
|
+
thankYou: "Gracias. Hemos recibido su solicitud y le contactaremos por correo electrónico con los próximos pasos.",
|
|
114318
|
+
validationSignatureRequired: "Escriba su nombre completo para firmar.",
|
|
114319
|
+
validationMustBeTrue: "Marque esta casilla para continuar.",
|
|
114320
|
+
validationRequired: "Complete este campo.",
|
|
114321
|
+
validationInvalid: "Revise este valor.",
|
|
114322
|
+
validationMinItems: "Agregue al menos {min} {noun}.",
|
|
114323
|
+
noApplicationFound: "No se encontró ninguna solicitud.",
|
|
114324
|
+
fixErrorsBeforeContinue: "Corrija los campos resaltados para continuar.",
|
|
114325
|
+
saving: "Guardando…",
|
|
114326
|
+
saveFailed: "No se pudo guardar su progreso. Inténtelo de nuevo.",
|
|
114327
|
+
submitFailed: "No se pudo enviar su solicitud. Revísela e inténtelo de nuevo.",
|
|
114328
|
+
uploading: "Subiendo…",
|
|
114329
|
+
uploadFailed: "No se pudo subir el archivo. Inténtelo de nuevo.",
|
|
114330
|
+
alreadyMemberTitle: "Ya eres miembro",
|
|
114331
|
+
alreadyMember: "Tu cuenta ya es miembro de este programa, por lo que no es necesario enviar una nueva solicitud.",
|
|
114332
|
+
status_draft: "Borrador",
|
|
114333
|
+
status_submitted: "Enviada",
|
|
114334
|
+
status_under_review: "En revisión",
|
|
114335
|
+
status_needs_more_info: "Requiere más información",
|
|
114336
|
+
status_interview_required: "Requiere entrevista",
|
|
114337
|
+
status_waitlisted: "En lista de espera",
|
|
114338
|
+
status_approved: "Aprobada",
|
|
114339
|
+
status_denied: "Denegada",
|
|
114340
|
+
status_withdrawn: "Retirada",
|
|
114341
|
+
status_pending: "Pendiente",
|
|
114342
|
+
status_accepted: "Aceptado",
|
|
114343
|
+
status_declined: "Rechazado",
|
|
114344
|
+
modeOpenTitle: "No se necesita solicitud",
|
|
114345
|
+
modeOpen: "Este programa acepta miembros directamente; puedes unirte sin completar una solicitud.",
|
|
114346
|
+
modeInviteOnlyTitle: "Solo por invitación",
|
|
114347
|
+
modeInviteOnly: "Este programa es solo por invitación. Contacta a la organización para recibir una invitación.",
|
|
114348
|
+
yourApplication: "Tu solicitud",
|
|
114349
|
+
submittedOn: "Enviada el {date}",
|
|
114350
|
+
approvedNotice: "Tu solicitud ha sido aprobada. ¡Bienvenido!",
|
|
114351
|
+
waitlistedNotice: "Tu solicitud está en lista de espera. Te avisaremos cuando haya un lugar.",
|
|
114352
|
+
renewalDeadline: "Fecha límite de renovación: {date}",
|
|
114353
|
+
renewalWindow: "Período de renovación: {start} – {end}",
|
|
114354
|
+
renew: "Renovar solicitud",
|
|
114355
|
+
renewing: "Preparando renovación…",
|
|
114356
|
+
renewFailed: "No se pudo iniciar la renovación. Inténtalo de nuevo.",
|
|
114357
|
+
withdraw: "Retirar solicitud",
|
|
114358
|
+
withdrawing: "Retirando…",
|
|
114359
|
+
withdrawConfirm: "¿Retirar esta solicitud?",
|
|
114360
|
+
otherApplications: "Tus otras solicitudes ({count})",
|
|
114361
|
+
applicationN: "Solicitud n.º {id}",
|
|
114362
|
+
needsMoreInfoBanner: "La escuela solicitó más información. Actualiza tus respuestas y vuelve a enviar.",
|
|
114363
|
+
saveDraft: "Guardar borrador",
|
|
114364
|
+
savingDraft: "Guardando…",
|
|
114365
|
+
draftSaved: "Borrador guardado",
|
|
114366
|
+
openSubmissionTitle: "Ya tienes una solicitud abierta",
|
|
114367
|
+
openSubmission: "Ya existe una solicitud abierta en este formulario. Consulta su estado en lugar de iniciar otra.",
|
|
114368
|
+
reapplyCooldownTitle: "Espera antes de volver a solicitar",
|
|
114369
|
+
reapplyCooldown: "Una decisión reciente sobre este formulario aún está en período de espera. Inténtalo más tarde.",
|
|
114370
|
+
renewalWindowBlockedTitle: "Período prioritario de renovación",
|
|
114371
|
+
renewalWindowBlocked: "Las nuevas solicitudes abren después del período prioritario para familias que regresan. Vuelve más tarde.",
|
|
114372
|
+
cannotApplyTitle: "Solicitud no disponible",
|
|
114373
|
+
cannotApply: "No se puede iniciar una solicitud en este momento. Contacta a la organización si crees que es un error.",
|
|
114374
|
+
notFoundBody: "No hay ningún formulario de solicitud en esta dirección. Verifica el enlace o pide a la organización el correcto.",
|
|
114375
|
+
chooseFile: "Elegir archivo…",
|
|
114376
|
+
fileTooLarge: "Cada archivo debe pesar menos de {max} MB.",
|
|
114377
|
+
maxFileSizeHint: "Máx. {max} MB"
|
|
114378
|
+
};
|
|
114379
|
+
var profileApplicationsTab$1 = {
|
|
114380
|
+
statusAll: "Todas",
|
|
114381
|
+
statusSubmitted: "Enviadas",
|
|
114382
|
+
statusUnderReview: "En revisión",
|
|
114383
|
+
statusNeedsMoreInfo: "Falta info",
|
|
114384
|
+
statusInterviewRequired: "Entrevista",
|
|
114385
|
+
statusAccepted: "Aceptadas",
|
|
114386
|
+
statusWaitlisted: "Lista de espera",
|
|
114387
|
+
statusDeclined: "Rechazadas",
|
|
114388
|
+
editQuestions: "Editar preguntas",
|
|
114389
|
+
searchPlaceholder: "Buscar solicitantes o estudiantes",
|
|
114390
|
+
colApplicant: "Solicitante",
|
|
114391
|
+
colStudents: "Estudiantes",
|
|
114392
|
+
colSubmitted: "Enviada",
|
|
114393
|
+
colStatus: "Estado",
|
|
114394
|
+
noMatch: "Ninguna solicitud coincide con sus filtros.",
|
|
114395
|
+
statusDraft: "Borrador",
|
|
114396
|
+
statusApproved: "Aprobadas",
|
|
114397
|
+
statusDenied: "Denegadas",
|
|
114398
|
+
statusWithdrawn: "Retiradas",
|
|
114399
|
+
statusPending: "Pendiente",
|
|
114400
|
+
loadError: "No se pudieron cargar las solicitudes. Inténtelo de nuevo más tarde.",
|
|
114401
|
+
copyLink: "Copiar enlace",
|
|
114402
|
+
linkCopied: "Copiado",
|
|
114403
|
+
formsHeading: "Formularios de solicitud",
|
|
114404
|
+
formsDescription: "Todos los formularios de solicitud de esta plataforma. Uno es el predeterminado para los solicitantes.",
|
|
114405
|
+
blockedUsers: "Usuarios bloqueados",
|
|
114406
|
+
blockedUsersDescription: "Familias con la solicitud bloqueada. Desbloquear les permite volver a solicitar.",
|
|
114407
|
+
allSubmissions: "Todas las solicitudes ({count})",
|
|
114408
|
+
newForm: "Nuevo formulario",
|
|
114409
|
+
postApproval_enrolled: "Inscritos",
|
|
114410
|
+
postApproval_enrollment_in_progress: "Inscripción en curso",
|
|
114411
|
+
postApproval_parent_account: "Con cuenta del padre/madre",
|
|
114412
|
+
colFormName: "Formulario",
|
|
114413
|
+
colVersion: "Versión",
|
|
114414
|
+
colState: "Estado",
|
|
114415
|
+
colActions: "Acciones",
|
|
114416
|
+
untitledForm: "Formulario sin título",
|
|
114417
|
+
defaultBadge: "Predeterminado",
|
|
114418
|
+
formEnabled: "Habilitado",
|
|
114419
|
+
formDisabled: "Deshabilitado",
|
|
114420
|
+
viewSubmissions: "Solicitudes",
|
|
114421
|
+
editForm: "Editar",
|
|
114422
|
+
noForms: "Aún no hay formularios de solicitud. Crea uno para activar las solicitudes.",
|
|
114423
|
+
backToForms: "Volver a formularios",
|
|
114424
|
+
submissionsFor: "Solicitudes — {form}",
|
|
114425
|
+
allSubmissionsHeading: "Todas las solicitudes",
|
|
114426
|
+
candidateFilterPlaceholder: "Filtro de estudiante, ej. grade=6",
|
|
114427
|
+
sortLabel: "Ordenar",
|
|
114428
|
+
sortNewest: "Más recientes primero",
|
|
114429
|
+
sortOldest: "Más antiguas primero",
|
|
114430
|
+
sortRecentlySubmitted: "Enviadas recientemente",
|
|
114431
|
+
sortRecentlyModified: "Modificadas recientemente",
|
|
114432
|
+
sortStatus: "Por estado",
|
|
114433
|
+
backToSubmissions: "Volver a solicitudes",
|
|
114434
|
+
decide: "Decidir",
|
|
114435
|
+
decideAll: "Decidir para todos los estudiantes",
|
|
114436
|
+
decideFamilyTitle: "Decisión para toda la familia",
|
|
114437
|
+
decideStudentTitle: "Decidir — {name}",
|
|
114438
|
+
actionLabel: "Acción",
|
|
114439
|
+
actionStartReview: "Iniciar revisión",
|
|
114440
|
+
actionRequestInfo: "Pedir más información",
|
|
114441
|
+
actionRequireInterview: "Requerir entrevista",
|
|
114442
|
+
actionWaitlist: "Lista de espera",
|
|
114443
|
+
actionAccept: "Aceptar",
|
|
114444
|
+
actionDecline: "Rechazar",
|
|
114445
|
+
messageLabel: "Mensaje al solicitante",
|
|
114446
|
+
messagePlaceholder: "Opcional: si queda vacío se usa la plantilla del formulario.",
|
|
114447
|
+
internalNoteLabel: "Nota interna",
|
|
114448
|
+
blockOnDecline: "Bloquear también futuras solicitudes de esta familia",
|
|
114449
|
+
applyAction: "Aplicar",
|
|
114450
|
+
cancel: "Cancelar",
|
|
114451
|
+
transitionApplied: "Decisión aplicada.",
|
|
114452
|
+
illegalTransition: "Esa acción no está permitida desde el estado actual.",
|
|
114453
|
+
noCandidatesYet: "Todavía es un borrador; aún no hay nada que revisar.",
|
|
114454
|
+
actionFailed: "Algo salió mal. Inténtalo de nuevo.",
|
|
114455
|
+
submittedAt: "Enviada el {date}",
|
|
114456
|
+
studentsHeading: "Estudiantes",
|
|
114457
|
+
noStudentsYet: "Aún no hay registros de estudiantes (borrador sin enviar).",
|
|
114458
|
+
identityLinked: "Cuenta vinculada",
|
|
114459
|
+
identityInvited: "Invitación enviada",
|
|
114460
|
+
identityInviteMissing: "Falta invitación",
|
|
114461
|
+
identityAccountUnlinked: "Cuenta sin vincular",
|
|
114462
|
+
identityParentAccount: "Cuenta del padre/madre",
|
|
114463
|
+
phaseEnrolled: "Inscrito",
|
|
114464
|
+
phaseEnrollmentInProgress: "Inscripción en curso",
|
|
114465
|
+
sendInvite: "Enviar invitación",
|
|
114466
|
+
inviteSent: "Invitación enviada.",
|
|
114467
|
+
linkAccount: "Vincular cuenta",
|
|
114468
|
+
linkAccountTitle: "Vincular cuenta — {name}",
|
|
114469
|
+
accountEmailLabel: "Correo de la cuenta",
|
|
114470
|
+
accountLinked: "Cuenta vinculada.",
|
|
114471
|
+
userNotFound: "No se encontró una cuenta con ese correo.",
|
|
114472
|
+
answersHeading: "Respuestas",
|
|
114473
|
+
entryN: "Entrada {n}",
|
|
114474
|
+
maskedValue: "Oculto",
|
|
114475
|
+
sectionMasked: "La sección \"{section}\" está oculta: no tienes acceso a datos sensibles.",
|
|
114476
|
+
answerYes: "Sí",
|
|
114477
|
+
answerNo: "No",
|
|
114478
|
+
attachmentsHeading: "Adjuntos",
|
|
114479
|
+
downloadFailed: "No se pudo descargar el archivo.",
|
|
114480
|
+
blockingHeading: "Lista para enviar",
|
|
114481
|
+
noBlockingItems: "Nada bloquea el envío.",
|
|
114482
|
+
waive: "Eximir",
|
|
114483
|
+
reinstate: "Restablecer",
|
|
114484
|
+
overrideSubmit: "Enviar con excepciones",
|
|
114485
|
+
overrideSubmitTitle: "¿Enviar con excepciones?",
|
|
114486
|
+
overrideSubmitBody: "Se eximirá cada requisito faltante y la solicitud se enviará en nombre de la familia.",
|
|
114487
|
+
overrideSubmitted: "Solicitud enviada con excepciones.",
|
|
114488
|
+
placementsHeading: "Pruebas de nivelación",
|
|
114489
|
+
assignPlacement: "Asignar prueba",
|
|
114490
|
+
noPlacements: "Sin pruebas de nivelación asignadas.",
|
|
114491
|
+
placementResult: "Resultado",
|
|
114492
|
+
placementStatusLabel: "Estado de la prueba",
|
|
114493
|
+
placementUpdated: "Prueba actualizada.",
|
|
114494
|
+
placementAssigned: "Prueba asignada.",
|
|
114495
|
+
placementAgentLabel: "Agente de evaluación",
|
|
114496
|
+
selectAgentPlaceholder: "Selecciona un agente...",
|
|
114497
|
+
searchAgentsPlaceholder: "Buscar agentes...",
|
|
114498
|
+
loadingAgents: "Cargando agentes...",
|
|
114499
|
+
noAgentsFound: "No se encontraron agentes",
|
|
114500
|
+
clearAgent: "Quitar {agent}",
|
|
114501
|
+
placementSubjectLabel: "Materia",
|
|
114502
|
+
studentLabel: "Estudiante",
|
|
114503
|
+
placement_not_assigned: "Sin asignar",
|
|
114504
|
+
placement_assigned: "Asignada",
|
|
114505
|
+
placement_started: "Iniciada",
|
|
114506
|
+
placement_completed: "Completada",
|
|
114507
|
+
placement_reviewed: "Revisada",
|
|
114508
|
+
placement_confirmed: "Confirmada",
|
|
114509
|
+
assignmentsHeading: "Asignación de cursos",
|
|
114510
|
+
assignCourse: "Asignar curso",
|
|
114511
|
+
noAssignments: "Sin cursos asignados.",
|
|
114512
|
+
startDateLabel: "Fecha de inicio",
|
|
114513
|
+
endDateLabel: "Fecha de término",
|
|
114514
|
+
courseAssigned: "Curso asignado.",
|
|
114515
|
+
assignmentRemoved: "Asignación eliminada.",
|
|
114516
|
+
removeAssignment: "Eliminar asignación",
|
|
114517
|
+
alreadyAssigned: "Ese estudiante ya tiene una asignación activa para este curso.",
|
|
114518
|
+
coursePlatformMismatch: "Ese curso no pertenece a esta plataforma.",
|
|
114519
|
+
notesHeading: "Notas internas",
|
|
114520
|
+
addNote: "Agregar nota",
|
|
114521
|
+
addNotePlaceholder: "Escribe una nota interna… (nunca se muestra a los solicitantes)",
|
|
114522
|
+
saveNote: "Guardar",
|
|
114523
|
+
editNote: "Editar nota",
|
|
114524
|
+
deleteNote: "Eliminar nota",
|
|
114525
|
+
noteKindLabel: "Tipo de nota",
|
|
114526
|
+
noteKindGeneral: "General",
|
|
114527
|
+
noteKindInterview: "Entrevista",
|
|
114528
|
+
auditHeading: "Historial",
|
|
114529
|
+
noEvents: "Aún no hay eventos.",
|
|
114530
|
+
familyWide: "Familia",
|
|
114531
|
+
colUser: "Usuario",
|
|
114532
|
+
colReason: "Motivo",
|
|
114533
|
+
colBlockedBy: "Bloqueado por",
|
|
114534
|
+
noBlockedUsers: "No hay usuarios bloqueados.",
|
|
114535
|
+
unblock: "Desbloquear",
|
|
114536
|
+
unblocked: "Usuario desbloqueado.",
|
|
114537
|
+
copyFormLink: "Copiar enlace de solicitud de {form}",
|
|
114538
|
+
coursesLabel: "Cursos",
|
|
114539
|
+
removeCourse: "Quitar {course}"
|
|
114540
|
+
};
|
|
114541
|
+
var profileApplicationsQuestionsEditor$1 = {
|
|
114542
|
+
backToApplications: "Volver a las solicitudes",
|
|
114543
|
+
saveChanges: "Guardar cambios",
|
|
114544
|
+
savedToast: "Formulario de solicitud guardado",
|
|
114545
|
+
editApplicationQuestions: "Editar preguntas de la solicitud",
|
|
114546
|
+
repeatable: "Repetible",
|
|
114547
|
+
editQuestion: "Editar pregunta",
|
|
114548
|
+
removeQuestion: "Eliminar pregunta",
|
|
114549
|
+
required: "Obligatorio",
|
|
114550
|
+
addQuestion: "Agregar pregunta",
|
|
114551
|
+
addSection: "Agregar sección",
|
|
114552
|
+
saving: "Guardando…",
|
|
114553
|
+
saveFailedToast: "No se pudo guardar el formulario de solicitud",
|
|
114554
|
+
editorDescription: "Agregue, reordene o elimine las preguntas que responden los solicitantes al postular.",
|
|
114555
|
+
unsavedChanges: "Cambios sin guardar",
|
|
114556
|
+
selectFormLabel: "Formulario",
|
|
114557
|
+
untitledForm: "Formulario sin título",
|
|
114558
|
+
defaultBadge: "Predeterminado",
|
|
114559
|
+
makeDefault: "Convertir en formulario predeterminado",
|
|
114560
|
+
enabledLabel: "Aceptando solicitudes",
|
|
114561
|
+
versionLabel: "Versión {version}",
|
|
114562
|
+
formTitleLabel: "Título del formulario",
|
|
114563
|
+
formDescriptionLabel: "Descripción",
|
|
114564
|
+
sectionTitlePlaceholder: "Título de la sección",
|
|
114565
|
+
candidateBadge: "Estudiantes",
|
|
114566
|
+
candidateToggle: "Sección de estudiantes",
|
|
114567
|
+
moveUp: "Subir",
|
|
114568
|
+
moveDown: "Bajar",
|
|
114569
|
+
removeSection: "Eliminar sección",
|
|
114570
|
+
emptySection: "Aún no hay preguntas.",
|
|
114571
|
+
noSections: "Este formulario aún no tiene secciones. Agregue una para empezar a recopilar respuestas.",
|
|
114572
|
+
newSectionTitle: "Nueva sección",
|
|
114573
|
+
fieldLabelLabel: "Etiqueta",
|
|
114574
|
+
fieldTypeLabel: "Tipo",
|
|
114575
|
+
fieldPlaceholderLabel: "Texto de ejemplo",
|
|
114576
|
+
fieldHelpTextLabel: "Texto de ayuda",
|
|
114577
|
+
fieldOptionsLabel: "Opciones (una por línea, valor | etiqueta)",
|
|
114578
|
+
fieldContentLabel: "Contenido (markdown)",
|
|
114579
|
+
fieldRequiredLabel: "Obligatoria",
|
|
114580
|
+
fieldMustBeTrueLabel: "Debe marcarse para enviar",
|
|
114581
|
+
fieldKeyNote: "Clave de respuesta: {key}",
|
|
114582
|
+
cancel: "Cancelar",
|
|
114583
|
+
saveQuestion: "Guardar pregunta",
|
|
114584
|
+
fixValidation: "Corrija estos problemas para guardar:",
|
|
114585
|
+
loadError: "No se pudieron cargar los formularios de solicitud. Inténtelo de nuevo más tarde.",
|
|
114586
|
+
confirmRemoveSection: "¿Eliminar esta sección y todas sus preguntas?",
|
|
114587
|
+
confirmRemoveQuestion: "¿Eliminar esta pregunta?",
|
|
114588
|
+
confirmRemove: "Eliminar",
|
|
114589
|
+
collapseSection: "Contraer sección",
|
|
114590
|
+
expandSection: "Expandir sección"
|
|
113680
114591
|
};
|
|
113681
114592
|
var es = {
|
|
113682
114593
|
accessTabAddAccess: accessTabAddAccess$1,
|
|
@@ -113924,7 +114835,11 @@ var es = {
|
|
|
113924
114835
|
benchmarksDeleteItemConfirm: benchmarksDeleteItemConfirm$1,
|
|
113925
114836
|
benchmarksAddItemsModal: benchmarksAddItemsModal$1,
|
|
113926
114837
|
platformSidebar: platformSidebar$1,
|
|
113927
|
-
platformNavbar: platformNavbar$1
|
|
114838
|
+
platformNavbar: platformNavbar$1,
|
|
114839
|
+
placementExam: placementExam$1,
|
|
114840
|
+
applyApplyWizard: applyApplyWizard$1,
|
|
114841
|
+
profileApplicationsTab: profileApplicationsTab$1,
|
|
114842
|
+
profileApplicationsQuestionsEditor: profileApplicationsQuestionsEditor$1
|
|
113928
114843
|
};
|
|
113929
114844
|
|
|
113930
114845
|
var accessTabAddAccess = {
|
|
@@ -117120,7 +118035,9 @@ var profileAdmin = {
|
|
|
117120
118035
|
rolesDescription: "RBAC 角色是一组命名的权限集合(在策略中应用于资源的操作)。",
|
|
117121
118036
|
policiesDescription: "RBAC 策略将角色的权限授予一组用户或群组,作用于一组资源。资源是层级结构的:在 /platforms/{pk}/mentors/ 上授权涵盖所有 agent,而 /platforms/{pk}/mentors/{pk}/ 则限定到单个 agent。",
|
|
117122
118037
|
teamsDescription: "团队是一组精心筛选的用户,可与他人共享 — 例如,授予另一个用户查看团队分析数据或向其发送通知的权限。",
|
|
117123
|
-
alertsDescription: "警报将一组被监控用户与一个或多个观察者配对,当被监控用户发生受监控事件时,观察者会收到通知。每位观察者可订阅自己感兴趣的事件子集。"
|
|
118038
|
+
alertsDescription: "警报将一组被监控用户与一个或多个观察者配对,当被监控用户发生受监控事件时,观察者会收到通知。每位观察者可订阅自己感兴趣的事件子集。",
|
|
118039
|
+
applicationsTab: "申请",
|
|
118040
|
+
applicationsTabDescription: "查看向贵组织提交的申请,并编辑申请人申请时回答的问题。"
|
|
117124
118041
|
};
|
|
117125
118042
|
var profileAutoRechargeModal = {
|
|
117126
118043
|
title: "管理用量",
|
|
@@ -119376,7 +120293,306 @@ var platformNavbar = {
|
|
|
119376
120293
|
search: "搜索",
|
|
119377
120294
|
searchPlaceholder: "搜索",
|
|
119378
120295
|
logIn: "登录",
|
|
119379
|
-
signUpForFree: "免费注册"
|
|
120296
|
+
signUpForFree: "免费注册",
|
|
120297
|
+
placementExamsAriaLabel: "分班测评({count} 个可参加)",
|
|
120298
|
+
placementExamsHeader: "分班测评",
|
|
120299
|
+
placementExamFallback: "分班测评"
|
|
120300
|
+
};
|
|
120301
|
+
var placementExam = {
|
|
120302
|
+
title: "分班测评",
|
|
120303
|
+
back: "返回"
|
|
120304
|
+
};
|
|
120305
|
+
var applyApplyWizard = {
|
|
120306
|
+
application: "申请",
|
|
120307
|
+
selectAnOption: "请选择一个选项",
|
|
120308
|
+
stepProgress: "第 {current} 步,共 {total} 步",
|
|
120309
|
+
reviewAndSubmit: "查看并提交",
|
|
120310
|
+
checkYourAnswers: "请检查您的答案,然后提交申请。",
|
|
120311
|
+
bySubmitting: "提交即表示您确认所提供的信息准确无误。",
|
|
120312
|
+
student: "学生",
|
|
120313
|
+
item: "项目",
|
|
120314
|
+
remove: "移除",
|
|
120315
|
+
addAnotherStudent: "添加另一名学生",
|
|
120316
|
+
addAnotherItem: "添加另一项",
|
|
120317
|
+
back: "返回",
|
|
120318
|
+
"continue": "继续",
|
|
120319
|
+
submitApplication: "提交申请",
|
|
120320
|
+
requiredAsterisk: "必填字段以星号标记。",
|
|
120321
|
+
applicationSubmitted: "申请已提交",
|
|
120322
|
+
thankYou: "谢谢。我们已收到您的申请,将通过电子邮件跟进后续步骤。",
|
|
120323
|
+
validationSignatureRequired: "请输入您的全名以签名。",
|
|
120324
|
+
validationMustBeTrue: "请勾选此项以继续。",
|
|
120325
|
+
validationRequired: "请填写此字段。",
|
|
120326
|
+
validationInvalid: "请检查此值。",
|
|
120327
|
+
validationMinItems: "请至少添加 {min} 个{noun}。",
|
|
120328
|
+
noApplicationFound: "未找到申请。",
|
|
120329
|
+
fixErrorsBeforeContinue: "请修正标记的字段后再继续。",
|
|
120330
|
+
saving: "保存中…",
|
|
120331
|
+
saveFailed: "无法保存您的进度,请重试。",
|
|
120332
|
+
submitFailed: "无法提交您的申请,请检查后重试。",
|
|
120333
|
+
uploading: "上传中…",
|
|
120334
|
+
uploadFailed: "文件上传失败,请重试。",
|
|
120335
|
+
alreadyMemberTitle: "您已是成员",
|
|
120336
|
+
alreadyMember: "您的账户已经是该项目的成员,无需再次提交申请。",
|
|
120337
|
+
status_draft: "草稿",
|
|
120338
|
+
status_submitted: "已提交",
|
|
120339
|
+
status_under_review: "审核中",
|
|
120340
|
+
status_needs_more_info: "需补充信息",
|
|
120341
|
+
status_interview_required: "需要面试",
|
|
120342
|
+
status_waitlisted: "候补名单",
|
|
120343
|
+
status_approved: "已批准",
|
|
120344
|
+
status_denied: "已拒绝",
|
|
120345
|
+
status_withdrawn: "已撤回",
|
|
120346
|
+
status_pending: "待处理",
|
|
120347
|
+
status_accepted: "已录取",
|
|
120348
|
+
status_declined: "未录取",
|
|
120349
|
+
modeOpenTitle: "无需申请",
|
|
120350
|
+
modeOpen: "该项目可直接加入,无需填写申请表。",
|
|
120351
|
+
modeInviteOnlyTitle: "仅限邀请",
|
|
120352
|
+
modeInviteOnly: "该项目仅限受邀加入。请联系组织获取邀请。",
|
|
120353
|
+
yourApplication: "您的申请",
|
|
120354
|
+
submittedOn: "提交于 {date}",
|
|
120355
|
+
approvedNotice: "您的申请已获批准。欢迎!",
|
|
120356
|
+
waitlistedNotice: "您的申请已列入候补名单。有名额时我们会通知您。",
|
|
120357
|
+
renewalDeadline: "续期截止日期:{date}",
|
|
120358
|
+
renewalWindow: "续期窗口:{start} – {end}",
|
|
120359
|
+
renew: "续期申请",
|
|
120360
|
+
renewing: "正在准备续期…",
|
|
120361
|
+
renewFailed: "无法开始续期。请重试。",
|
|
120362
|
+
withdraw: "撤回申请",
|
|
120363
|
+
withdrawing: "正在撤回…",
|
|
120364
|
+
withdrawConfirm: "确定撤回此申请?",
|
|
120365
|
+
otherApplications: "您的其他申请({count})",
|
|
120366
|
+
applicationN: "申请 #{id}",
|
|
120367
|
+
needsMoreInfoBanner: "学校要求补充信息。请更新以下答案并重新提交。",
|
|
120368
|
+
saveDraft: "保存草稿",
|
|
120369
|
+
savingDraft: "保存中…",
|
|
120370
|
+
draftSaved: "草稿已保存",
|
|
120371
|
+
openSubmissionTitle: "您已有一份进行中的申请",
|
|
120372
|
+
openSubmission: "此表单已有一份进行中的申请。请查看其状态,而不是重新开始。",
|
|
120373
|
+
reapplyCooldownTitle: "请稍后再申请",
|
|
120374
|
+
reapplyCooldown: "此表单最近的决定仍在冷却期内。请稍后再试。",
|
|
120375
|
+
renewalWindowBlockedTitle: "优先续期窗口",
|
|
120376
|
+
renewalWindowBlocked: "新申请将在老生家庭优先窗口结束后开放。请稍后再来。",
|
|
120377
|
+
cannotApplyTitle: "无法申请",
|
|
120378
|
+
cannotApply: "目前无法开始申请。如果您认为这是错误,请联系组织。",
|
|
120379
|
+
notFoundBody: "此地址没有申请表。请检查链接,或向组织索取正确的链接。",
|
|
120380
|
+
chooseFile: "选择文件…",
|
|
120381
|
+
fileTooLarge: "每个文件必须小于 {max} MB。",
|
|
120382
|
+
maxFileSizeHint: "最大 {max} MB"
|
|
120383
|
+
};
|
|
120384
|
+
var profileApplicationsTab = {
|
|
120385
|
+
statusAll: "全部",
|
|
120386
|
+
statusSubmitted: "已提交",
|
|
120387
|
+
statusUnderReview: "审核中",
|
|
120388
|
+
statusNeedsMoreInfo: "需补充信息",
|
|
120389
|
+
statusInterviewRequired: "面试",
|
|
120390
|
+
statusAccepted: "已录取",
|
|
120391
|
+
statusWaitlisted: "候补名单",
|
|
120392
|
+
statusDeclined: "已拒绝",
|
|
120393
|
+
editQuestions: "编辑问题",
|
|
120394
|
+
searchPlaceholder: "搜索申请人或学生",
|
|
120395
|
+
colApplicant: "申请人",
|
|
120396
|
+
colStudents: "学生",
|
|
120397
|
+
colSubmitted: "提交时间",
|
|
120398
|
+
colStatus: "状态",
|
|
120399
|
+
noMatch: "没有符合筛选条件的申请。",
|
|
120400
|
+
statusDraft: "草稿",
|
|
120401
|
+
statusApproved: "已批准",
|
|
120402
|
+
statusDenied: "已否决",
|
|
120403
|
+
statusWithdrawn: "已撤回",
|
|
120404
|
+
statusPending: "待处理",
|
|
120405
|
+
loadError: "无法加载申请,请稍后重试。",
|
|
120406
|
+
copyLink: "复制链接",
|
|
120407
|
+
linkCopied: "已复制",
|
|
120408
|
+
formsHeading: "申请表",
|
|
120409
|
+
formsDescription: "此平台的所有申请表。其中一个是申请人默认使用的表单。",
|
|
120410
|
+
blockedUsers: "已屏蔽用户",
|
|
120411
|
+
blockedUsersDescription: "被禁止再次申请的家庭。解除屏蔽后可重新申请。",
|
|
120412
|
+
allSubmissions: "全部提交({count})",
|
|
120413
|
+
newForm: "新建表单",
|
|
120414
|
+
postApproval_enrolled: "已注册",
|
|
120415
|
+
postApproval_enrollment_in_progress: "注册进行中",
|
|
120416
|
+
postApproval_parent_account: "使用家长账户",
|
|
120417
|
+
colFormName: "表单",
|
|
120418
|
+
colVersion: "版本",
|
|
120419
|
+
colState: "状态",
|
|
120420
|
+
colActions: "操作",
|
|
120421
|
+
untitledForm: "未命名表单",
|
|
120422
|
+
defaultBadge: "默认",
|
|
120423
|
+
formEnabled: "已启用",
|
|
120424
|
+
formDisabled: "已停用",
|
|
120425
|
+
viewSubmissions: "查看提交",
|
|
120426
|
+
editForm: "编辑",
|
|
120427
|
+
noForms: "还没有申请表。创建一个以开启申请功能。",
|
|
120428
|
+
backToForms: "返回表单列表",
|
|
120429
|
+
submissionsFor: "提交 — {form}",
|
|
120430
|
+
allSubmissionsHeading: "全部提交",
|
|
120431
|
+
candidateFilterPlaceholder: "学生筛选,如 grade=6",
|
|
120432
|
+
sortLabel: "排序",
|
|
120433
|
+
sortNewest: "最新优先",
|
|
120434
|
+
sortOldest: "最早优先",
|
|
120435
|
+
sortRecentlySubmitted: "最近提交",
|
|
120436
|
+
sortRecentlyModified: "最近修改",
|
|
120437
|
+
sortStatus: "按状态",
|
|
120438
|
+
backToSubmissions: "返回提交列表",
|
|
120439
|
+
decide: "决定",
|
|
120440
|
+
decideAll: "为所有学生做决定",
|
|
120441
|
+
decideFamilyTitle: "家庭整体决定",
|
|
120442
|
+
decideStudentTitle: "决定 — {name}",
|
|
120443
|
+
actionLabel: "操作",
|
|
120444
|
+
actionStartReview: "开始审核",
|
|
120445
|
+
actionRequestInfo: "要求补充信息",
|
|
120446
|
+
actionRequireInterview: "要求面试",
|
|
120447
|
+
actionWaitlist: "列入候补",
|
|
120448
|
+
actionAccept: "录取",
|
|
120449
|
+
actionDecline: "拒绝",
|
|
120450
|
+
messageLabel: "给申请人的消息",
|
|
120451
|
+
messagePlaceholder: "可选 — 留空时使用表单模板。",
|
|
120452
|
+
internalNoteLabel: "内部备注",
|
|
120453
|
+
blockOnDecline: "同时禁止该家庭再次申请",
|
|
120454
|
+
applyAction: "应用",
|
|
120455
|
+
cancel: "取消",
|
|
120456
|
+
transitionApplied: "决定已应用。",
|
|
120457
|
+
illegalTransition: "当前状态不允许该操作。",
|
|
120458
|
+
noCandidatesYet: "这仍是草稿 — 尚无可审核内容。",
|
|
120459
|
+
actionFailed: "出现问题。请重试。",
|
|
120460
|
+
submittedAt: "提交于 {date}",
|
|
120461
|
+
studentsHeading: "学生",
|
|
120462
|
+
noStudentsYet: "尚无学生记录(草稿未提交)。",
|
|
120463
|
+
identityLinked: "账户已关联",
|
|
120464
|
+
identityInvited: "邀请已发送",
|
|
120465
|
+
identityInviteMissing: "缺少邀请",
|
|
120466
|
+
identityAccountUnlinked: "账户未关联",
|
|
120467
|
+
identityParentAccount: "家长账户",
|
|
120468
|
+
phaseEnrolled: "已注册",
|
|
120469
|
+
phaseEnrollmentInProgress: "注册进行中",
|
|
120470
|
+
sendInvite: "发送邀请",
|
|
120471
|
+
inviteSent: "邀请已发送。",
|
|
120472
|
+
linkAccount: "关联账户",
|
|
120473
|
+
linkAccountTitle: "关联账户 — {name}",
|
|
120474
|
+
accountEmailLabel: "账户邮箱",
|
|
120475
|
+
accountLinked: "账户已关联。",
|
|
120476
|
+
userNotFound: "未找到使用该邮箱的账户。",
|
|
120477
|
+
answersHeading: "答案",
|
|
120478
|
+
entryN: "条目 {n}",
|
|
120479
|
+
maskedValue: "已隐藏",
|
|
120480
|
+
sectionMasked: "“{section}”部分已隐藏 — 您没有敏感数据访问权限。",
|
|
120481
|
+
answerYes: "是",
|
|
120482
|
+
answerNo: "否",
|
|
120483
|
+
attachmentsHeading: "附件",
|
|
120484
|
+
downloadFailed: "无法下载文件。",
|
|
120485
|
+
blockingHeading: "提交检查清单",
|
|
120486
|
+
noBlockingItems: "没有阻碍提交的事项。",
|
|
120487
|
+
waive: "豁免",
|
|
120488
|
+
reinstate: "恢复",
|
|
120489
|
+
overrideSubmit: "例外提交",
|
|
120490
|
+
overrideSubmitTitle: "确认例外提交?",
|
|
120491
|
+
overrideSubmitBody: "所有缺失的必填项将被豁免,并代表该家庭提交申请。",
|
|
120492
|
+
overrideSubmitted: "申请已例外提交。",
|
|
120493
|
+
placementsHeading: "分级测试",
|
|
120494
|
+
assignPlacement: "分配测试",
|
|
120495
|
+
noPlacements: "尚未分配分级测试。",
|
|
120496
|
+
placementResult: "结果",
|
|
120497
|
+
placementStatusLabel: "测试状态",
|
|
120498
|
+
placementUpdated: "测试已更新。",
|
|
120499
|
+
placementAssigned: "测试已分配。",
|
|
120500
|
+
placementAgentLabel: "分班测评智能体",
|
|
120501
|
+
selectAgentPlaceholder: "选择智能体...",
|
|
120502
|
+
searchAgentsPlaceholder: "搜索智能体...",
|
|
120503
|
+
loadingAgents: "正在加载智能体...",
|
|
120504
|
+
noAgentsFound: "未找到智能体",
|
|
120505
|
+
clearAgent: "移除 {agent}",
|
|
120506
|
+
placementSubjectLabel: "科目",
|
|
120507
|
+
studentLabel: "学生",
|
|
120508
|
+
placement_not_assigned: "未分配",
|
|
120509
|
+
placement_assigned: "已分配",
|
|
120510
|
+
placement_started: "已开始",
|
|
120511
|
+
placement_completed: "已完成",
|
|
120512
|
+
placement_reviewed: "已审阅",
|
|
120513
|
+
placement_confirmed: "已确认",
|
|
120514
|
+
assignmentsHeading: "课程分配",
|
|
120515
|
+
assignCourse: "分配课程",
|
|
120516
|
+
noAssignments: "尚未分配课程。",
|
|
120517
|
+
startDateLabel: "开始日期",
|
|
120518
|
+
endDateLabel: "结束日期",
|
|
120519
|
+
courseAssigned: "课程已分配。",
|
|
120520
|
+
assignmentRemoved: "分配已移除。",
|
|
120521
|
+
removeAssignment: "移除分配",
|
|
120522
|
+
alreadyAssigned: "该学生已有此课程的有效分配。",
|
|
120523
|
+
coursePlatformMismatch: "该课程不属于此平台。",
|
|
120524
|
+
notesHeading: "内部备注",
|
|
120525
|
+
addNote: "添加备注",
|
|
120526
|
+
addNotePlaceholder: "撰写内部备注…(申请人永远不会看到)",
|
|
120527
|
+
saveNote: "保存",
|
|
120528
|
+
editNote: "编辑备注",
|
|
120529
|
+
deleteNote: "删除备注",
|
|
120530
|
+
noteKindLabel: "备注类型",
|
|
120531
|
+
noteKindGeneral: "一般",
|
|
120532
|
+
noteKindInterview: "面试",
|
|
120533
|
+
auditHeading: "历史记录",
|
|
120534
|
+
noEvents: "暂无事件。",
|
|
120535
|
+
familyWide: "家庭",
|
|
120536
|
+
colUser: "用户",
|
|
120537
|
+
colReason: "原因",
|
|
120538
|
+
colBlockedBy: "屏蔽人",
|
|
120539
|
+
noBlockedUsers: "没有已屏蔽的用户。",
|
|
120540
|
+
unblock: "解除屏蔽",
|
|
120541
|
+
unblocked: "已解除屏蔽。",
|
|
120542
|
+
copyFormLink: "复制 {form} 的申请链接",
|
|
120543
|
+
coursesLabel: "课程",
|
|
120544
|
+
removeCourse: "移除 {course}"
|
|
120545
|
+
};
|
|
120546
|
+
var profileApplicationsQuestionsEditor = {
|
|
120547
|
+
backToApplications: "返回申请列表",
|
|
120548
|
+
saveChanges: "保存更改",
|
|
120549
|
+
savedToast: "申请表单已保存",
|
|
120550
|
+
editApplicationQuestions: "编辑申请问题",
|
|
120551
|
+
repeatable: "可重复",
|
|
120552
|
+
editQuestion: "编辑问题",
|
|
120553
|
+
removeQuestion: "移除问题",
|
|
120554
|
+
required: "必填",
|
|
120555
|
+
addQuestion: "添加问题",
|
|
120556
|
+
addSection: "添加板块",
|
|
120557
|
+
saving: "保存中…",
|
|
120558
|
+
saveFailedToast: "无法保存申请表单",
|
|
120559
|
+
editorDescription: "添加、重新排序或移除申请人在申请时回答的问题。",
|
|
120560
|
+
unsavedChanges: "有未保存的更改",
|
|
120561
|
+
selectFormLabel: "表单",
|
|
120562
|
+
untitledForm: "未命名表单",
|
|
120563
|
+
defaultBadge: "默认",
|
|
120564
|
+
makeDefault: "设为默认表单",
|
|
120565
|
+
enabledLabel: "开放申请",
|
|
120566
|
+
versionLabel: "版本 {version}",
|
|
120567
|
+
formTitleLabel: "表单标题",
|
|
120568
|
+
formDescriptionLabel: "描述",
|
|
120569
|
+
sectionTitlePlaceholder: "板块标题",
|
|
120570
|
+
candidateBadge: "学生",
|
|
120571
|
+
candidateToggle: "学生板块",
|
|
120572
|
+
moveUp: "上移",
|
|
120573
|
+
moveDown: "下移",
|
|
120574
|
+
removeSection: "移除板块",
|
|
120575
|
+
emptySection: "暂无问题。",
|
|
120576
|
+
noSections: "此表单尚无板块。添加一个板块以开始收集回答。",
|
|
120577
|
+
newSectionTitle: "新板块",
|
|
120578
|
+
fieldLabelLabel: "标签",
|
|
120579
|
+
fieldTypeLabel: "类型",
|
|
120580
|
+
fieldPlaceholderLabel: "占位文本",
|
|
120581
|
+
fieldHelpTextLabel: "帮助文本",
|
|
120582
|
+
fieldOptionsLabel: "选项(每行一个,值 | 标签)",
|
|
120583
|
+
fieldContentLabel: "内容(Markdown)",
|
|
120584
|
+
fieldRequiredLabel: "必填",
|
|
120585
|
+
fieldMustBeTrueLabel: "必须勾选才能提交",
|
|
120586
|
+
fieldKeyNote: "答案键:{key}",
|
|
120587
|
+
cancel: "取消",
|
|
120588
|
+
saveQuestion: "保存问题",
|
|
120589
|
+
fixValidation: "请修正以下问题后再保存:",
|
|
120590
|
+
loadError: "无法加载申请表单,请稍后重试。",
|
|
120591
|
+
confirmRemoveSection: "移除此板块及其所有问题?",
|
|
120592
|
+
confirmRemoveQuestion: "移除此问题?",
|
|
120593
|
+
confirmRemove: "移除",
|
|
120594
|
+
collapseSection: "折叠板块",
|
|
120595
|
+
expandSection: "展开板块"
|
|
119380
120596
|
};
|
|
119381
120597
|
var zh = {
|
|
119382
120598
|
accessTabAddAccess: accessTabAddAccess,
|
|
@@ -119624,7 +120840,11 @@ var zh = {
|
|
|
119624
120840
|
benchmarksDeleteItemConfirm: benchmarksDeleteItemConfirm,
|
|
119625
120841
|
benchmarksAddItemsModal: benchmarksAddItemsModal,
|
|
119626
120842
|
platformSidebar: platformSidebar,
|
|
119627
|
-
platformNavbar: platformNavbar
|
|
120843
|
+
platformNavbar: platformNavbar,
|
|
120844
|
+
placementExam: placementExam,
|
|
120845
|
+
applyApplyWizard: applyApplyWizard,
|
|
120846
|
+
profileApplicationsTab: profileApplicationsTab,
|
|
120847
|
+
profileApplicationsQuestionsEditor: profileApplicationsQuestionsEditor
|
|
119628
120848
|
};
|
|
119629
120849
|
|
|
119630
120850
|
/**
|
|
@@ -174277,7 +175497,7 @@ function DetailRow({ label, children }) {
|
|
|
174277
175497
|
return (jsxs("div", { className: "flex items-center justify-between gap-4 py-2", children: [jsx("span", { className: "text-sm text-gray-500", children: label }), jsx("span", { className: "text-sm font-medium text-gray-900 text-right", children: children })] }));
|
|
174278
175498
|
}
|
|
174279
175499
|
|
|
174280
|
-
const PAGE_SIZE$
|
|
175500
|
+
const PAGE_SIZE$9 = 8;
|
|
174281
175501
|
const CONTENT_TYPES = [
|
|
174282
175502
|
{ value: 'courses', labelKey: 'gradebookGradebookTab.typeCourses', icon: GraduationCap },
|
|
174283
175503
|
{ value: 'programs', labelKey: 'gradebookGradebookTab.typePrograms', icon: Layers },
|
|
@@ -174308,14 +175528,14 @@ function useGradebookData(type, org, username, page, search) {
|
|
|
174308
175528
|
username,
|
|
174309
175529
|
query: {
|
|
174310
175530
|
page,
|
|
174311
|
-
page_size: PAGE_SIZE$
|
|
175531
|
+
page_size: PAGE_SIZE$9,
|
|
174312
175532
|
platform_key: org,
|
|
174313
175533
|
...(trimmed ? { search: trimmed } : {}),
|
|
174314
175534
|
},
|
|
174315
175535
|
}, { skip: type !== 'courses', refetchOnMountOrArgChange: true });
|
|
174316
175536
|
const programsQ = useGetUserEnrolledProgramsQuery([{ username, platformKey: org }], { skip: type !== 'programs' });
|
|
174317
175537
|
const pathwaysQ = useGetUserCatalogPathwaysQuery({ username, platform_key: org }, { skip: type !== 'pathways' });
|
|
174318
|
-
const credentialsQ = useGetUsersAsAssertionsQuery({ platformKey: org, userId: username, page, pageSize: PAGE_SIZE$
|
|
175538
|
+
const credentialsQ = useGetUsersAsAssertionsQuery({ platformKey: org, userId: username, page, pageSize: PAGE_SIZE$9 }, { skip: type !== 'credentials', refetchOnMountOrArgChange: true });
|
|
174319
175539
|
const earnedQ = useGetUserEarnedSkillsQuery([{ org, userId: username }], { skip: type !== 'skills' });
|
|
174320
175540
|
const reportedQ = useGetUserReportedSkillsQuery([{ username }], { skip: type !== 'skills' });
|
|
174321
175541
|
const desiredQ = useGetUserDesiredSkillsQuery([{ username }], { skip: type !== 'skills' });
|
|
@@ -174325,12 +175545,12 @@ function useGradebookData(type, org, username, page, search) {
|
|
|
174325
175545
|
const filtered = trimmed
|
|
174326
175546
|
? rows.filter((row) => row.name.toLowerCase().includes(trimmed.toLowerCase()))
|
|
174327
175547
|
: rows;
|
|
174328
|
-
const start = (page - 1) * PAGE_SIZE$
|
|
175548
|
+
const start = (page - 1) * PAGE_SIZE$9;
|
|
174329
175549
|
return {
|
|
174330
|
-
rows: filtered.slice(start, start + PAGE_SIZE$
|
|
175550
|
+
rows: filtered.slice(start, start + PAGE_SIZE$9),
|
|
174331
175551
|
total: filtered.length,
|
|
174332
175552
|
hasPrev: page > 1,
|
|
174333
|
-
hasNext: start + PAGE_SIZE$
|
|
175553
|
+
hasNext: start + PAGE_SIZE$9 < filtered.length,
|
|
174334
175554
|
};
|
|
174335
175555
|
};
|
|
174336
175556
|
switch (type) {
|
|
@@ -174387,7 +175607,7 @@ function useGradebookData(type, org, username, page, search) {
|
|
|
174387
175607
|
rows,
|
|
174388
175608
|
total,
|
|
174389
175609
|
hasPrev: page > 1,
|
|
174390
|
-
hasNext: page * PAGE_SIZE$
|
|
175610
|
+
hasNext: page * PAGE_SIZE$9 < total,
|
|
174391
175611
|
isLoading: credentialsQ.isLoading,
|
|
174392
175612
|
isFetching: credentialsQ.isFetching,
|
|
174393
175613
|
isError: credentialsQ.isError,
|
|
@@ -178222,7 +179442,7 @@ function PoliciesTab({ tenant }) {
|
|
|
178222
179442
|
: t('tabsPoliciestab.createPolicy')] }) }) }), editing && (policyDetails === null || policyDetails === void 0 ? void 0 : policyDetails.is_internal) && (jsx(TooltipContent, { children: jsx("p", { children: t('tabsPoliciestab.cannotEditInternal') }) }))] }) })] })] }) }), jsx(ResourcePickerModal, { open: isResourcePickerOpen, onOpenChange: setIsResourcePickerOpen, tenant: tenant, onSelect: addPickedResource })] }));
|
|
178223
179443
|
}
|
|
178224
179444
|
|
|
178225
|
-
const PAGE_SIZE$
|
|
179445
|
+
const PAGE_SIZE$8 = 10;
|
|
178226
179446
|
function normalizeEvents(events) {
|
|
178227
179447
|
if (!events)
|
|
178228
179448
|
return [];
|
|
@@ -178271,8 +179491,8 @@ function AlertsTab({ tenant }) {
|
|
|
178271
179491
|
const { data: watchedGroupsData, isLoading, isError, refetch, } = useGetWatchedGroupsQuery({
|
|
178272
179492
|
params: {
|
|
178273
179493
|
platform_key: tenant,
|
|
178274
|
-
limit: PAGE_SIZE$
|
|
178275
|
-
offset: (page - 1) * PAGE_SIZE$
|
|
179494
|
+
limit: PAGE_SIZE$8,
|
|
179495
|
+
offset: (page - 1) * PAGE_SIZE$8,
|
|
178276
179496
|
},
|
|
178277
179497
|
});
|
|
178278
179498
|
const groups = useMemo(() => {
|
|
@@ -178285,7 +179505,7 @@ function AlertsTab({ tenant }) {
|
|
|
178285
179505
|
return results;
|
|
178286
179506
|
}, [watchedGroupsData, debouncedSearchQuery]);
|
|
178287
179507
|
const totalPages = watchedGroupsData
|
|
178288
|
-
? Math.max(1, Math.ceil(watchedGroupsData.count / PAGE_SIZE$
|
|
179508
|
+
? Math.max(1, Math.ceil(watchedGroupsData.count / PAGE_SIZE$8))
|
|
178289
179509
|
: 0;
|
|
178290
179510
|
const handlePageChange = async (newPage) => {
|
|
178291
179511
|
setPage(newPage);
|
|
@@ -178594,7 +179814,7 @@ function AlertsTab({ tenant }) {
|
|
|
178594
179814
|
setDeletingGroupId(null);
|
|
178595
179815
|
}
|
|
178596
179816
|
}
|
|
178597
|
-
return (jsxs("div", { className: "mt-4", children: [jsxs("div", { className: "flex mb-6 gap-2 flex-row items-center justify-between", children: [jsxs("div", { className: "relative max-w-sm w-full sm:w-auto", children: [jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" }), jsx(Input, { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t('tabsAlertstab.searchAlertsPlaceholder'), "aria-label": t('tabsAlertstab.searchAlertsAriaLabel'), className: "pl-10 pr-10 focus:ring-blue-500 focus:border-blue-500 h-[35px]" }), isLoading && (jsx(LoaderCircle, { "data-testid": "search-spinner", className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400 animate-spin", "aria-hidden": "true" }))] }), jsx("div", { className: "flex gap-2", children: jsx(Button$1, { onClick: openNew, "aria-label": t('tabsAlertstab.newAlertAriaLabel'), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white h-[35px]", children: t('tabsAlertstab.newAlert') }) })] }), isLoading && (jsx("div", { className: "flex justify-center items-center h-64", children: jsx("div", { className: "py-8 text-center text-sm text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.loading') }) })), isError && (jsx("div", { className: "py-8 text-center text-sm text-red-500", children: t('tabsAlertstab.failedToLoadAlerts') })), !isLoading && !isError && (jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.columnName') }), jsx("th", { className: "px-6 py-3 text-right font-medium text-gray-500 dark:text-gray-400 w-[120px]" })] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [groups.map((g) => (jsxs("tr", { children: [jsx("td", { className: "px-6 py-4 text-gray-900 dark:text-gray-100 text-sm", children: g.name }), jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex gap-2 justify-end", children: [jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => openEdit(g), disabled: deletingGroupId === g.id, "aria-label": t('tabsAlertstab.editAlertAriaLabel', { name: g.name }), children: jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }) }), jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => onDelete(g), className: "text-red-600 hover:text-red-700", disabled: deletingGroupId === g.id, "aria-label": t('tabsAlertstab.deleteAlertAriaLabel', { name: g.name }), children: deletingGroupId === g.id ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" })) : (jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" })) })] }) })] }, g.id))), groups.length === 0 && (jsx("tr", { children: jsx("td", { colSpan: 2, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.noAlertsFound') }) }))] })] }) })), !isLoading && !isError && (jsx(AdvancedPagination, { variant: "footer", className: "-mx-6 -mb-6 mt-4 rounded-b-lg", totalPages: totalPages, currentPage: page, onPageChange: (newPage) => handlePageChange(newPage), totalCount: watchedGroupsData === null || watchedGroupsData === void 0 ? void 0 : watchedGroupsData.count, itemsPerPage: PAGE_SIZE$
|
|
179817
|
+
return (jsxs("div", { className: "mt-4", children: [jsxs("div", { className: "flex mb-6 gap-2 flex-row items-center justify-between", children: [jsxs("div", { className: "relative max-w-sm w-full sm:w-auto", children: [jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" }), jsx(Input, { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t('tabsAlertstab.searchAlertsPlaceholder'), "aria-label": t('tabsAlertstab.searchAlertsAriaLabel'), className: "pl-10 pr-10 focus:ring-blue-500 focus:border-blue-500 h-[35px]" }), isLoading && (jsx(LoaderCircle, { "data-testid": "search-spinner", className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400 animate-spin", "aria-hidden": "true" }))] }), jsx("div", { className: "flex gap-2", children: jsx(Button$1, { onClick: openNew, "aria-label": t('tabsAlertstab.newAlertAriaLabel'), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white h-[35px]", children: t('tabsAlertstab.newAlert') }) })] }), isLoading && (jsx("div", { className: "flex justify-center items-center h-64", children: jsx("div", { className: "py-8 text-center text-sm text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.loading') }) })), isError && (jsx("div", { className: "py-8 text-center text-sm text-red-500", children: t('tabsAlertstab.failedToLoadAlerts') })), !isLoading && !isError && (jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.columnName') }), jsx("th", { className: "px-6 py-3 text-right font-medium text-gray-500 dark:text-gray-400 w-[120px]" })] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [groups.map((g) => (jsxs("tr", { children: [jsx("td", { className: "px-6 py-4 text-gray-900 dark:text-gray-100 text-sm", children: g.name }), jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex gap-2 justify-end", children: [jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => openEdit(g), disabled: deletingGroupId === g.id, "aria-label": t('tabsAlertstab.editAlertAriaLabel', { name: g.name }), children: jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }) }), jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => onDelete(g), className: "text-red-600 hover:text-red-700", disabled: deletingGroupId === g.id, "aria-label": t('tabsAlertstab.deleteAlertAriaLabel', { name: g.name }), children: deletingGroupId === g.id ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" })) : (jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" })) })] }) })] }, g.id))), groups.length === 0 && (jsx("tr", { children: jsx("td", { colSpan: 2, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.noAlertsFound') }) }))] })] }) })), !isLoading && !isError && (jsx(AdvancedPagination, { variant: "footer", className: "-mx-6 -mb-6 mt-4 rounded-b-lg", totalPages: totalPages, currentPage: page, onPageChange: (newPage) => handlePageChange(newPage), totalCount: watchedGroupsData === null || watchedGroupsData === void 0 ? void 0 : watchedGroupsData.count, itemsPerPage: PAGE_SIZE$8, itemLabel: "alerts" })), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { "aria-describedby": undefined, className: "max-w-2xl max-h-[90vh] overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: editingGroup ? t('tabsAlertstab.editAlert') : t('tabsAlertstab.newAlert') }) }), jsxs("div", { className: "space-y-5", children: [jsxs("div", { className: "grid gap-1.5", children: [jsx(Label$1, { htmlFor: "wg-name", children: t('tabsAlertstab.nameLabel') }), jsx(Input, { id: "wg-name", value: name, onChange: (e) => setName(e.target.value), placeholder: t('tabsAlertstab.enterAlertNamePlaceholder'), required: true })] }), jsxs("div", { className: "grid gap-1.5", children: [jsx(Label$1, { children: t('tabsAlertstab.watchedUsersLabel') }), jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.watchedUsersDescription') }), watchedUsers.length > 0 ? (jsx("div", { className: "space-y-2 max-h-48 overflow-y-auto border border-gray-200 dark:border-gray-700 rounded-md p-2", children: watchedUsers.map((user) => (jsxs("div", { className: "flex items-center justify-between bg-gray-50 dark:bg-gray-800 p-2 rounded-md", children: [jsxs("div", { className: "flex flex-col", children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: user.name || user.username }), user.email && (jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: user.email }))] }), jsx(Button$1, { type: "button", variant: "ghost", size: "sm", onClick: () => removeWatchedUserDraft(user.id), className: "h-8 w-8 p-0 text-red-600 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-900/20", "aria-label": t('tabsAlertstab.removeWatchedUserAriaLabel', {
|
|
178598
179818
|
username: user.username,
|
|
178599
179819
|
}), children: jsx(X$2, { className: "h-4 w-4" }) })] }, user.id))) })) : (jsx("div", { className: "text-sm text-gray-500 dark:text-gray-400 p-4 text-center border border-dashed border-gray-300 dark:border-gray-600 rounded-md", children: t('tabsAlertstab.noWatchedUsersAdded') })), jsxs("div", { className: "relative", children: [jsx(Input, { value: watchedUserSearch, onChange: (e) => {
|
|
178600
179820
|
setWatchedUserSearch(e.target.value);
|
|
@@ -178645,14 +179865,1466 @@ function AlertsTab({ tenant }) {
|
|
|
178645
179865
|
availableWatcherUsers.length === 0 && (jsx("div", { className: "absolute top-full left-0 right-0 mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg p-3 z-50", children: jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.noUsersFound') }) }))] })] })] }), formError && (jsx("div", { className: "mt-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-md", children: jsx("p", { className: "text-sm text-red-600 dark:text-red-400", children: formError }) })), jsxs(DialogFooter, { className: "mt-6", children: [jsx(Button$1, { variant: "outline", onClick: () => setIsOpen(false), children: t('tabsAlertstab.cancel') }), jsxs(Button$1, { onClick: onSubmit, disabled: isSubmitting, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white", children: [isSubmitting ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin mr-2", "aria-hidden": "true" })) : null, editingGroup ? t('tabsAlertstab.saveAlert') : t('tabsAlertstab.createAlert')] })] })] }) })] }));
|
|
178646
179866
|
}
|
|
178647
179867
|
|
|
179868
|
+
/**
|
|
179869
|
+
* Drop every trailing `/` from a URL (or path) so a segment can be appended
|
|
179870
|
+
* without doubling the separator. Scans backwards in linear time on purpose:
|
|
179871
|
+
* the equivalent `/\/+$/` regex backtracks quadratically on caller-supplied
|
|
179872
|
+
* strings holding a long run of slashes (ReDoS).
|
|
179873
|
+
*/
|
|
179874
|
+
function trimTrailingSlashes(value) {
|
|
179875
|
+
let end = value.length;
|
|
179876
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47 /* '/' */)
|
|
179877
|
+
end -= 1;
|
|
179878
|
+
return end === value.length ? value : value.slice(0, end);
|
|
179879
|
+
}
|
|
179880
|
+
|
|
179881
|
+
var COLLAPSIBLE_NAME = "Collapsible";
|
|
179882
|
+
var [createCollapsibleContext] = createContextScope$1(COLLAPSIBLE_NAME);
|
|
179883
|
+
var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
|
|
179884
|
+
var Collapsible$1 = React.forwardRef(
|
|
179885
|
+
(props, forwardedRef) => {
|
|
179886
|
+
const {
|
|
179887
|
+
__scopeCollapsible,
|
|
179888
|
+
open: openProp,
|
|
179889
|
+
defaultOpen,
|
|
179890
|
+
disabled,
|
|
179891
|
+
onOpenChange,
|
|
179892
|
+
...collapsibleProps
|
|
179893
|
+
} = props;
|
|
179894
|
+
const [open, setOpen] = useControllableState$1({
|
|
179895
|
+
prop: openProp,
|
|
179896
|
+
defaultProp: defaultOpen ?? false,
|
|
179897
|
+
onChange: onOpenChange,
|
|
179898
|
+
caller: COLLAPSIBLE_NAME
|
|
179899
|
+
});
|
|
179900
|
+
return /* @__PURE__ */ jsx(
|
|
179901
|
+
CollapsibleProvider,
|
|
179902
|
+
{
|
|
179903
|
+
scope: __scopeCollapsible,
|
|
179904
|
+
disabled,
|
|
179905
|
+
contentId: useId$1(),
|
|
179906
|
+
open,
|
|
179907
|
+
onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
179908
|
+
children: /* @__PURE__ */ jsx(
|
|
179909
|
+
Primitive$2.div,
|
|
179910
|
+
{
|
|
179911
|
+
"data-state": getState$1(open),
|
|
179912
|
+
"data-disabled": disabled ? "" : void 0,
|
|
179913
|
+
...collapsibleProps,
|
|
179914
|
+
ref: forwardedRef
|
|
179915
|
+
}
|
|
179916
|
+
)
|
|
179917
|
+
}
|
|
179918
|
+
);
|
|
179919
|
+
}
|
|
179920
|
+
);
|
|
179921
|
+
Collapsible$1.displayName = COLLAPSIBLE_NAME;
|
|
179922
|
+
var TRIGGER_NAME$1 = "CollapsibleTrigger";
|
|
179923
|
+
var CollapsibleTrigger$1 = React.forwardRef(
|
|
179924
|
+
(props, forwardedRef) => {
|
|
179925
|
+
const { __scopeCollapsible, ...triggerProps } = props;
|
|
179926
|
+
const context = useCollapsibleContext(TRIGGER_NAME$1, __scopeCollapsible);
|
|
179927
|
+
return /* @__PURE__ */ jsx(
|
|
179928
|
+
Primitive$2.button,
|
|
179929
|
+
{
|
|
179930
|
+
type: "button",
|
|
179931
|
+
"aria-controls": context.contentId,
|
|
179932
|
+
"aria-expanded": context.open || false,
|
|
179933
|
+
"data-state": getState$1(context.open),
|
|
179934
|
+
"data-disabled": context.disabled ? "" : void 0,
|
|
179935
|
+
disabled: context.disabled,
|
|
179936
|
+
...triggerProps,
|
|
179937
|
+
ref: forwardedRef,
|
|
179938
|
+
onClick: composeEventHandlers$1(props.onClick, context.onOpenToggle)
|
|
179939
|
+
}
|
|
179940
|
+
);
|
|
179941
|
+
}
|
|
179942
|
+
);
|
|
179943
|
+
CollapsibleTrigger$1.displayName = TRIGGER_NAME$1;
|
|
179944
|
+
var CONTENT_NAME$2 = "CollapsibleContent";
|
|
179945
|
+
var CollapsibleContent$1 = React.forwardRef(
|
|
179946
|
+
(props, forwardedRef) => {
|
|
179947
|
+
const { forceMount, ...contentProps } = props;
|
|
179948
|
+
const context = useCollapsibleContext(CONTENT_NAME$2, props.__scopeCollapsible);
|
|
179949
|
+
return /* @__PURE__ */ jsx(Presence$2, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
|
|
179950
|
+
}
|
|
179951
|
+
);
|
|
179952
|
+
CollapsibleContent$1.displayName = CONTENT_NAME$2;
|
|
179953
|
+
var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
|
|
179954
|
+
const { __scopeCollapsible, present, children, ...contentProps } = props;
|
|
179955
|
+
const context = useCollapsibleContext(CONTENT_NAME$2, __scopeCollapsible);
|
|
179956
|
+
const [isPresent, setIsPresent] = React.useState(present);
|
|
179957
|
+
const ref = React.useRef(null);
|
|
179958
|
+
const composedRefs = useComposedRefs$1(forwardedRef, ref);
|
|
179959
|
+
const heightRef = React.useRef(0);
|
|
179960
|
+
const height = heightRef.current;
|
|
179961
|
+
const widthRef = React.useRef(0);
|
|
179962
|
+
const width = widthRef.current;
|
|
179963
|
+
const isOpen = context.open || isPresent;
|
|
179964
|
+
const isMountAnimationPreventedRef = React.useRef(isOpen);
|
|
179965
|
+
const originalStylesRef = React.useRef(void 0);
|
|
179966
|
+
React.useEffect(() => {
|
|
179967
|
+
const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
|
|
179968
|
+
return () => cancelAnimationFrame(rAF);
|
|
179969
|
+
}, []);
|
|
179970
|
+
useLayoutEffect2$1(() => {
|
|
179971
|
+
const node = ref.current;
|
|
179972
|
+
if (node) {
|
|
179973
|
+
originalStylesRef.current = originalStylesRef.current || {
|
|
179974
|
+
transitionDuration: node.style.transitionDuration,
|
|
179975
|
+
animationName: node.style.animationName
|
|
179976
|
+
};
|
|
179977
|
+
node.style.transitionDuration = "0s";
|
|
179978
|
+
node.style.animationName = "none";
|
|
179979
|
+
const rect = node.getBoundingClientRect();
|
|
179980
|
+
heightRef.current = rect.height;
|
|
179981
|
+
widthRef.current = rect.width;
|
|
179982
|
+
if (!isMountAnimationPreventedRef.current) {
|
|
179983
|
+
node.style.transitionDuration = originalStylesRef.current.transitionDuration;
|
|
179984
|
+
node.style.animationName = originalStylesRef.current.animationName;
|
|
179985
|
+
}
|
|
179986
|
+
setIsPresent(present);
|
|
179987
|
+
}
|
|
179988
|
+
}, [context.open, present]);
|
|
179989
|
+
return /* @__PURE__ */ jsx(
|
|
179990
|
+
Primitive$2.div,
|
|
179991
|
+
{
|
|
179992
|
+
"data-state": getState$1(context.open),
|
|
179993
|
+
"data-disabled": context.disabled ? "" : void 0,
|
|
179994
|
+
id: context.contentId,
|
|
179995
|
+
hidden: !isOpen,
|
|
179996
|
+
...contentProps,
|
|
179997
|
+
ref: composedRefs,
|
|
179998
|
+
style: {
|
|
179999
|
+
[`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
|
|
180000
|
+
[`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
|
|
180001
|
+
...props.style
|
|
180002
|
+
},
|
|
180003
|
+
children: isOpen && children
|
|
180004
|
+
}
|
|
180005
|
+
);
|
|
180006
|
+
});
|
|
180007
|
+
function getState$1(open) {
|
|
180008
|
+
return open ? "open" : "closed";
|
|
180009
|
+
}
|
|
180010
|
+
var Root$6 = Collapsible$1;
|
|
180011
|
+
|
|
180012
|
+
const Collapsible = Root$6;
|
|
180013
|
+
const CollapsibleTrigger = CollapsibleTrigger$1;
|
|
180014
|
+
const CollapsibleContent = CollapsibleContent$1;
|
|
180015
|
+
|
|
180016
|
+
// Pure logic behind the application form (questions) editor. No React here so
|
|
180017
|
+
// it stays unit testable. The backend validates schemas on save and remains
|
|
180018
|
+
// authoritative; these helpers only shape the editor's local state.
|
|
180019
|
+
/** Field types the editor offers, in menu order. */
|
|
180020
|
+
const EDITABLE_FIELD_TYPES = [
|
|
180021
|
+
'text',
|
|
180022
|
+
'textarea',
|
|
180023
|
+
'email',
|
|
180024
|
+
'phone',
|
|
180025
|
+
'number',
|
|
180026
|
+
'date',
|
|
180027
|
+
'boolean',
|
|
180028
|
+
'select',
|
|
180029
|
+
'multi_select',
|
|
180030
|
+
'file',
|
|
180031
|
+
'signature',
|
|
180032
|
+
'info',
|
|
180033
|
+
];
|
|
180034
|
+
/** Field types that carry an options list. */
|
|
180035
|
+
function hasOptions(type) {
|
|
180036
|
+
return type === 'select' || type === 'multi_select';
|
|
180037
|
+
}
|
|
180038
|
+
/** The forms list endpoint may return a plain array or a results envelope. */
|
|
180039
|
+
function normalizeFormsList(data) {
|
|
180040
|
+
if (Array.isArray(data))
|
|
180041
|
+
return data;
|
|
180042
|
+
if (data && typeof data === 'object') {
|
|
180043
|
+
const results = data.results;
|
|
180044
|
+
if (Array.isArray(results))
|
|
180045
|
+
return results;
|
|
180046
|
+
}
|
|
180047
|
+
return [];
|
|
180048
|
+
}
|
|
180049
|
+
/** Pick the form to open first: the default one, else the first listed. */
|
|
180050
|
+
function pickInitialForm(forms) {
|
|
180051
|
+
var _a;
|
|
180052
|
+
return (_a = forms.find((f) => f.is_default)) !== null && _a !== void 0 ? _a : forms[0];
|
|
180053
|
+
}
|
|
180054
|
+
/** Defensive read of a form record's schema into the editor shape. */
|
|
180055
|
+
function coerceSchema(record) {
|
|
180056
|
+
var _a, _b;
|
|
180057
|
+
const schema = ((_a = record === null || record === void 0 ? void 0 : record.schema) !== null && _a !== void 0 ? _a : {});
|
|
180058
|
+
return {
|
|
180059
|
+
...schema,
|
|
180060
|
+
title: typeof schema.title === 'string' ? schema.title : ((_b = record === null || record === void 0 ? void 0 : record.name) !== null && _b !== void 0 ? _b : ''),
|
|
180061
|
+
sections: Array.isArray(schema.sections) ? schema.sections : [],
|
|
180062
|
+
};
|
|
180063
|
+
}
|
|
180064
|
+
/** Starter schema for a platform's first form. */
|
|
180065
|
+
function emptyForm(title) {
|
|
180066
|
+
return { title, sections: [] };
|
|
180067
|
+
}
|
|
180068
|
+
/**
|
|
180069
|
+
* Derive a stable snake_case key from a label, unique within `taken`.
|
|
180070
|
+
* Falls back to a prefix when the label has no usable characters.
|
|
180071
|
+
*/
|
|
180072
|
+
function slugifyKey(label, taken, fallback = 'field') {
|
|
180073
|
+
const base = label
|
|
180074
|
+
.toLowerCase()
|
|
180075
|
+
.normalize('NFKD')
|
|
180076
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
180077
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
180078
|
+
.replace(/^_+|_+$/g, '')
|
|
180079
|
+
.slice(0, 40) || fallback;
|
|
180080
|
+
const used = new Set(taken);
|
|
180081
|
+
if (!used.has(base))
|
|
180082
|
+
return base;
|
|
180083
|
+
let n = 2;
|
|
180084
|
+
while (used.has(`${base}_${n}`))
|
|
180085
|
+
n += 1;
|
|
180086
|
+
return `${base}_${n}`;
|
|
180087
|
+
}
|
|
180088
|
+
/** Immutably replace the section at `index`. */
|
|
180089
|
+
function replaceSection(form, index, section) {
|
|
180090
|
+
const sections = form.sections.slice();
|
|
180091
|
+
sections[index] = section;
|
|
180092
|
+
return { ...form, sections };
|
|
180093
|
+
}
|
|
180094
|
+
/** Immutably move an item up or down inside a list. */
|
|
180095
|
+
function moveItem(list, index, delta) {
|
|
180096
|
+
const target = index + delta;
|
|
180097
|
+
if (target < 0 || target >= list.length)
|
|
180098
|
+
return list;
|
|
180099
|
+
const next = list.slice();
|
|
180100
|
+
const [item] = next.splice(index, 1);
|
|
180101
|
+
next.splice(target, 0, item);
|
|
180102
|
+
return next;
|
|
180103
|
+
}
|
|
180104
|
+
/**
|
|
180105
|
+
* Parse the options textarea (one option per line, "value | label" or a bare
|
|
180106
|
+
* label whose value is derived) into select options.
|
|
180107
|
+
*/
|
|
180108
|
+
function parseOptionsText(text) {
|
|
180109
|
+
return text
|
|
180110
|
+
.split('\n')
|
|
180111
|
+
.map((line) => line.trim())
|
|
180112
|
+
.filter(Boolean)
|
|
180113
|
+
.map((line) => {
|
|
180114
|
+
const pipe = line.indexOf('|');
|
|
180115
|
+
if (pipe !== -1) {
|
|
180116
|
+
const value = line.slice(0, pipe).trim();
|
|
180117
|
+
const label = line.slice(pipe + 1).trim();
|
|
180118
|
+
return { value: value || label, label: label || value };
|
|
180119
|
+
}
|
|
180120
|
+
return { value: slugifyKey(line, [], 'option'), label: line };
|
|
180121
|
+
});
|
|
180122
|
+
}
|
|
180123
|
+
/** Render options back into the textarea format ("value | label" per line). */
|
|
180124
|
+
function formatOptionsText(options) {
|
|
180125
|
+
return (options !== null && options !== void 0 ? options : []).map((o) => `${o.value} | ${o.label}`).join('\n');
|
|
180126
|
+
}
|
|
180127
|
+
/**
|
|
180128
|
+
* Read the structured error list out of a rejected save. The API nests it
|
|
180129
|
+
* under an `errors` key (matching the submit-error shape); tolerate a bare
|
|
180130
|
+
* array or a stringified body as well.
|
|
180131
|
+
*/
|
|
180132
|
+
function extractSchemaErrors(errorData) {
|
|
180133
|
+
let data = errorData;
|
|
180134
|
+
if (typeof data === 'string') {
|
|
180135
|
+
try {
|
|
180136
|
+
data = JSON.parse(data);
|
|
180137
|
+
}
|
|
180138
|
+
catch (_a) {
|
|
180139
|
+
return [];
|
|
180140
|
+
}
|
|
180141
|
+
}
|
|
180142
|
+
if (Array.isArray(data))
|
|
180143
|
+
return data.filter(isSchemaError);
|
|
180144
|
+
if (data && typeof data === 'object') {
|
|
180145
|
+
const errors = data.errors;
|
|
180146
|
+
if (Array.isArray(errors))
|
|
180147
|
+
return errors.filter(isSchemaError);
|
|
180148
|
+
}
|
|
180149
|
+
return [];
|
|
180150
|
+
}
|
|
180151
|
+
function isSchemaError(item) {
|
|
180152
|
+
return !!item && typeof item === 'object' && typeof item.error === 'string';
|
|
180153
|
+
}
|
|
180154
|
+
|
|
180155
|
+
/**
|
|
180156
|
+
* The "Edit Questions" view: the form manager for an application form.
|
|
180157
|
+
* Loads the platform's forms, edits the selected form's schema (sections and
|
|
180158
|
+
* fields) locally, and saves through the form-management endpoints. The
|
|
180159
|
+
* backend validates on save; its structured errors are surfaced above the
|
|
180160
|
+
* sections.
|
|
180161
|
+
*/
|
|
180162
|
+
function QuestionsEditor({ platformKey, onBack, initialFormId = null, startNew = false, }) {
|
|
180163
|
+
var _a, _b, _c;
|
|
180164
|
+
const t = useT();
|
|
180165
|
+
const { data: formsData, isLoading: formsLoading, error: formsError, } = useGetApplicationFormsQuery({ platform_key: platformKey }, { refetchOnMountOrArgChange: true });
|
|
180166
|
+
const forms = useMemo(() => normalizeFormsList(formsData), [formsData]);
|
|
180167
|
+
const [selectedId, setSelectedId] = useState(initialFormId);
|
|
180168
|
+
const [creatingNew, setCreatingNew] = useState(startNew);
|
|
180169
|
+
const activeId = creatingNew ? null : ((_b = selectedId !== null && selectedId !== void 0 ? selectedId : (_a = pickInitialForm(forms)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null);
|
|
180170
|
+
const activeRecord = forms.find((f) => f.id === activeId);
|
|
180171
|
+
const { data: detailData, isFetching: detailFetching } = useGetApplicationFormDetailQuery({ form_id: activeId, platform_key: platformKey }, { skip: activeId == null, refetchOnMountOrArgChange: true });
|
|
180172
|
+
const detail = (activeId == null ? undefined : detailData);
|
|
180173
|
+
const [draft, setDraft] = useState(null);
|
|
180174
|
+
const [enabled, setEnabled] = useState(false);
|
|
180175
|
+
const [makeDefault, setMakeDefault] = useState(false);
|
|
180176
|
+
const [dirty, setDirty] = useState(false);
|
|
180177
|
+
const [errors, setErrors] = useState([]);
|
|
180178
|
+
const [fieldDialog, setFieldDialog] = useState(null);
|
|
180179
|
+
/** Collapse state per section key; sections not listed are expanded. */
|
|
180180
|
+
const [expandedSections, setExpandedSections] = useState({});
|
|
180181
|
+
/** Section whose title input should take focus when it mounts (a new one). */
|
|
180182
|
+
const [focusSectionKey, setFocusSectionKey] = useState(null);
|
|
180183
|
+
// Seed the local draft whenever the loaded form (or its saved version) changes.
|
|
180184
|
+
useEffect(() => {
|
|
180185
|
+
if (creatingNew) {
|
|
180186
|
+
setDraft(emptyForm(''));
|
|
180187
|
+
setEnabled(false);
|
|
180188
|
+
setMakeDefault(false);
|
|
180189
|
+
setDirty(false);
|
|
180190
|
+
setErrors([]);
|
|
180191
|
+
}
|
|
180192
|
+
else if (detail) {
|
|
180193
|
+
setDraft(coerceSchema(detail));
|
|
180194
|
+
setEnabled(!!detail.enabled);
|
|
180195
|
+
setMakeDefault(false);
|
|
180196
|
+
setDirty(false);
|
|
180197
|
+
setErrors([]);
|
|
180198
|
+
setExpandedSections({});
|
|
180199
|
+
}
|
|
180200
|
+
}, [creatingNew, detail]);
|
|
180201
|
+
// A platform with no forms goes straight to the create flow.
|
|
180202
|
+
useEffect(() => {
|
|
180203
|
+
if (!formsLoading && forms.length === 0)
|
|
180204
|
+
setCreatingNew(true);
|
|
180205
|
+
}, [formsLoading, forms.length]);
|
|
180206
|
+
const update = (fn) => {
|
|
180207
|
+
setDraft((current) => (current ? fn(current) : current));
|
|
180208
|
+
setDirty(true);
|
|
180209
|
+
};
|
|
180210
|
+
const [createForm, { isLoading: creating }] = useCreateApplicationFormMutation();
|
|
180211
|
+
const [updateForm, { isLoading: updating }] = useUpdateApplicationFormMutation();
|
|
180212
|
+
const saving = creating || updating;
|
|
180213
|
+
const handleSave = async () => {
|
|
180214
|
+
if (!draft)
|
|
180215
|
+
return;
|
|
180216
|
+
const payload = {
|
|
180217
|
+
platform_key: platformKey,
|
|
180218
|
+
name: draft.title,
|
|
180219
|
+
schema: draft,
|
|
180220
|
+
enabled,
|
|
180221
|
+
...(makeDefault ? { is_default: true } : {}),
|
|
180222
|
+
};
|
|
180223
|
+
try {
|
|
180224
|
+
if (activeId == null) {
|
|
180225
|
+
const created = (await createForm(payload).unwrap());
|
|
180226
|
+
setCreatingNew(false);
|
|
180227
|
+
if ((created === null || created === void 0 ? void 0 : created.id) != null)
|
|
180228
|
+
setSelectedId(created.id);
|
|
180229
|
+
}
|
|
180230
|
+
else {
|
|
180231
|
+
await updateForm({ form_id: activeId, ...payload }).unwrap();
|
|
180232
|
+
}
|
|
180233
|
+
setErrors([]);
|
|
180234
|
+
setDirty(false);
|
|
180235
|
+
toast.success(t('profileApplicationsQuestionsEditor.savedToast'));
|
|
180236
|
+
}
|
|
180237
|
+
catch (err) {
|
|
180238
|
+
const list = extractSchemaErrors(err === null || err === void 0 ? void 0 : err.data);
|
|
180239
|
+
setErrors(list);
|
|
180240
|
+
toast.error(t('profileApplicationsQuestionsEditor.saveFailedToast'));
|
|
180241
|
+
}
|
|
180242
|
+
};
|
|
180243
|
+
const addSection = () => {
|
|
180244
|
+
if (!draft)
|
|
180245
|
+
return;
|
|
180246
|
+
const title = t('profileApplicationsQuestionsEditor.newSectionTitle');
|
|
180247
|
+
const key = slugifyKey(title, draft.sections.map((s) => s.key), 'section');
|
|
180248
|
+
// Collapse the existing sections so the new one gets the focus.
|
|
180249
|
+
setExpandedSections({
|
|
180250
|
+
...Object.fromEntries(draft.sections.map((s) => [s.key, false])),
|
|
180251
|
+
[key]: true,
|
|
180252
|
+
});
|
|
180253
|
+
setFocusSectionKey(key);
|
|
180254
|
+
update((form) => ({
|
|
180255
|
+
...form,
|
|
180256
|
+
sections: [...form.sections, { key, title, fields: [] }],
|
|
180257
|
+
}));
|
|
180258
|
+
};
|
|
180259
|
+
const saveField = (state, field) => update((form) => {
|
|
180260
|
+
const section = form.sections[state.sectionIndex];
|
|
180261
|
+
const fields = state.fieldIndex == null
|
|
180262
|
+
? [...section.fields, field]
|
|
180263
|
+
: section.fields.map((f, i) => (i === state.fieldIndex ? field : f));
|
|
180264
|
+
return replaceSection(form, state.sectionIndex, { ...section, fields });
|
|
180265
|
+
});
|
|
180266
|
+
if (formsLoading || (activeId != null && detailFetching && !draft)) {
|
|
180267
|
+
return (jsxs("div", { className: "space-y-4", children: [jsx(Skeleton, { className: "h-8 w-56" }), jsx(Skeleton, { className: "h-40 w-full" }), jsx(Skeleton, { className: "h-40 w-full" })] }));
|
|
180268
|
+
}
|
|
180269
|
+
if (formsError) {
|
|
180270
|
+
return (jsxs("div", { className: "space-y-4 pt-4", children: [jsx(BackButton$1, { onBack: onBack }), jsx("p", { className: "text-sm text-red-600", children: t('profileApplicationsQuestionsEditor.loadError') })] }));
|
|
180271
|
+
}
|
|
180272
|
+
if (!draft)
|
|
180273
|
+
return null;
|
|
180274
|
+
const dialogSection = fieldDialog ? draft.sections[fieldDialog.sectionIndex] : null;
|
|
180275
|
+
return (jsxs("div", { className: "space-y-5 pt-4", children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsx(BackButton$1, { onBack: onBack }), jsxs("div", { className: "flex items-center gap-3", children: [dirty && (jsx("span", { className: "text-xs text-amber-600", children: t('profileApplicationsQuestionsEditor.unsavedChanges') })), jsx(Button$1, { onClick: handleSave, disabled: saving, className: "bg-gradient-to-r from-blue-600 to-blue-400 text-white hover:opacity-90", children: saving
|
|
180276
|
+
? t('profileApplicationsQuestionsEditor.saving')
|
|
180277
|
+
: t('profileApplicationsQuestionsEditor.saveChanges') })] })] }), jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [jsxs("div", { children: [jsx("h4", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: t('profileApplicationsQuestionsEditor.editApplicationQuestions') }), jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t('profileApplicationsQuestionsEditor.editorDescription') })] }), forms.length > 1 && (jsxs(Select$1, { value: activeId != null ? String(activeId) : '', onValueChange: (value) => {
|
|
180278
|
+
setCreatingNew(false);
|
|
180279
|
+
setSelectedId(Number(value));
|
|
180280
|
+
}, children: [jsx(SelectTrigger, { className: "w-56", "aria-label": t('profileApplicationsQuestionsEditor.selectFormLabel'), children: jsx(SelectValue, {}) }), jsx(SelectContent, { children: forms.map((form) => (jsxs(SelectItem, { value: String(form.id), children: [form.name || t('profileApplicationsQuestionsEditor.untitledForm'), form.is_default
|
|
180281
|
+
? ` (${t('profileApplicationsQuestionsEditor.defaultBadge')})`
|
|
180282
|
+
: ''] }, form.id))) })] }))] }), jsxs("div", { className: "space-y-4 rounded-xl border border-gray-200 bg-white p-5 shadow-sm dark:border-gray-700 dark:bg-gray-900", children: [jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "application-form-title", children: t('profileApplicationsQuestionsEditor.formTitleLabel') }), jsx(Input, { id: "application-form-title", value: draft.title, onChange: (e) => update((form) => ({ ...form, title: e.target.value })) })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "application-form-description", children: t('profileApplicationsQuestionsEditor.formDescriptionLabel') }), jsx(Input, { id: "application-form-description", value: (_c = draft.description) !== null && _c !== void 0 ? _c : '', onChange: (e) => update((form) => ({ ...form, description: e.target.value })) })] })] }), jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300", children: [jsx(Switch, { checked: enabled, onCheckedChange: (value) => {
|
|
180283
|
+
setEnabled(value === true);
|
|
180284
|
+
setDirty(true);
|
|
180285
|
+
}, className: "data-[state=checked]:bg-blue-600" }), t('profileApplicationsQuestionsEditor.enabledLabel')] }), (activeRecord === null || activeRecord === void 0 ? void 0 : activeRecord.is_default) ? (jsx(Badge, { variant: "outline", className: "text-[11px] font-medium text-gray-500", children: t('profileApplicationsQuestionsEditor.defaultBadge') })) : (forms.length > 0 && (jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300", children: [jsx(Checkbox, { checked: makeDefault, onCheckedChange: (value) => {
|
|
180286
|
+
setMakeDefault(value === true);
|
|
180287
|
+
setDirty(true);
|
|
180288
|
+
} }), t('profileApplicationsQuestionsEditor.makeDefault')] }))), (detail === null || detail === void 0 ? void 0 : detail.version) != null && (jsx("span", { className: "text-xs text-gray-400", children: t('profileApplicationsQuestionsEditor.versionLabel', { version: detail.version }) }))] })] }), errors.length > 0 && (jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700", children: [jsx("p", { className: "mb-1 font-medium", children: t('profileApplicationsQuestionsEditor.fixValidation') }), jsx("ul", { className: "list-inside list-disc space-y-0.5", children: errors.map((error, i) => (jsxs("li", { children: [[error.section, error.field].filter(Boolean).join('.'), error.section || error.field ? ': ' : '', error.error] }, i))) })] })), jsxs("div", { className: "space-y-5", children: [draft.sections.length === 0 && (jsx("p", { className: "rounded-lg border border-dashed border-gray-300 p-6 text-center text-sm text-gray-400", children: t('profileApplicationsQuestionsEditor.noSections') })), draft.sections.map((section, sectionIndex) => {
|
|
180289
|
+
var _a;
|
|
180290
|
+
return (jsx(SectionCard, { section: section, expanded: (_a = expandedSections[section.key]) !== null && _a !== void 0 ? _a : true, autoFocusTitle: focusSectionKey === section.key, onToggle: (open) => setExpandedSections((prev) => ({ ...prev, [section.key]: open })), onChange: (next) => update((form) => replaceSection(form, sectionIndex, next)), onRemove: () => update((form) => ({
|
|
180291
|
+
...form,
|
|
180292
|
+
sections: form.sections.filter((_, i) => i !== sectionIndex),
|
|
180293
|
+
})), onMove: (delta) => update((form) => ({
|
|
180294
|
+
...form,
|
|
180295
|
+
sections: moveItem(form.sections, sectionIndex, delta),
|
|
180296
|
+
})), onMakeCandidate: (value) => update((form) => ({
|
|
180297
|
+
...form,
|
|
180298
|
+
// At most one candidate section: turning one on clears the rest.
|
|
180299
|
+
sections: form.sections.map((s, i) => ({
|
|
180300
|
+
...s,
|
|
180301
|
+
candidate: i === sectionIndex ? value : value ? false : s.candidate,
|
|
180302
|
+
})),
|
|
180303
|
+
})), onEditField: (fieldIndex) => setFieldDialog({ sectionIndex, fieldIndex }), onAddField: () => setFieldDialog({ sectionIndex, fieldIndex: null }) }, section.key || sectionIndex));
|
|
180304
|
+
}), jsxs(Button$1, { variant: "outline", className: "w-full border-dashed text-gray-600 hover:border-blue-300 hover:text-blue-700 dark:text-gray-300 dark:hover:border-blue-800 dark:hover:text-blue-400", onClick: addSection, children: [jsx(Plus, { className: "mr-1.5 h-4 w-4" }), t('profileApplicationsQuestionsEditor.addSection')] })] }), fieldDialog && dialogSection && (jsx(FieldDialog, { section: dialogSection, field: fieldDialog.fieldIndex != null ? dialogSection.fields[fieldDialog.fieldIndex] : null, onClose: () => setFieldDialog(null), onSave: (field) => {
|
|
180305
|
+
saveField(fieldDialog, field);
|
|
180306
|
+
setFieldDialog(null);
|
|
180307
|
+
} }))] }));
|
|
180308
|
+
}
|
|
180309
|
+
function BackButton$1({ onBack }) {
|
|
180310
|
+
const t = useT();
|
|
180311
|
+
return (jsxs("button", { type: "button", onClick: onBack, className: "inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 dark:text-gray-300", children: [jsx(ArrowLeft, { className: "h-4 w-4" }), t('profileApplicationsQuestionsEditor.backToApplications')] }));
|
|
180312
|
+
}
|
|
180313
|
+
function SectionCard({ section, expanded, autoFocusTitle = false, onToggle, onChange, onRemove, onMove, onMakeCandidate, onEditField, onAddField, }) {
|
|
180314
|
+
var _a;
|
|
180315
|
+
const t = useT();
|
|
180316
|
+
return (jsxs(Collapsible, { open: expanded, onOpenChange: onToggle, className: "rounded-xl border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-900", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 border-b border-gray-100 px-5 py-3 dark:border-gray-800", children: [jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [jsx(CollapsibleTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", className: "h-7 w-7 shrink-0 p-0 text-gray-400", "aria-label": t(expanded
|
|
180317
|
+
? 'profileApplicationsQuestionsEditor.collapseSection'
|
|
180318
|
+
: 'profileApplicationsQuestionsEditor.expandSection'), children: jsx(ChevronDown, { className: `h-4 w-4 transition-transform ${expanded ? '' : '-rotate-90'}` }) }) }), jsx(Input, { value: (_a = section.title) !== null && _a !== void 0 ? _a : '', placeholder: t('profileApplicationsQuestionsEditor.sectionTitlePlaceholder'), "aria-label": t('profileApplicationsQuestionsEditor.sectionTitlePlaceholder'), className: "h-8 max-w-xs border-transparent px-1.5 text-sm font-semibold shadow-none hover:border-gray-200 focus:border-gray-300",
|
|
180319
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
180320
|
+
autoFocus: autoFocusTitle, onFocus: (e) => autoFocusTitle && e.target.select(), onChange: (e) => onChange({ ...section, title: e.target.value }) }), section.repeatable && (jsx(Badge, { variant: "outline", className: "text-[11px] font-medium text-gray-500", children: t('profileApplicationsQuestionsEditor.repeatable') })), section.candidate && (jsx(Badge, { variant: "outline", className: "text-[11px] font-medium text-blue-600", children: t('profileApplicationsQuestionsEditor.candidateBadge') }))] }), jsxs("div", { className: "flex items-center gap-3", children: [jsxs("label", { className: "flex items-center gap-1.5 text-xs text-gray-500", children: [jsx(Checkbox, { checked: !!section.repeatable, onCheckedChange: (value) => onChange({ ...section, repeatable: value === true }) }), t('profileApplicationsQuestionsEditor.repeatable')] }), jsxs("label", { className: "flex items-center gap-1.5 text-xs text-gray-500", children: [jsx(Checkbox, { checked: !!section.candidate, onCheckedChange: (value) => onMakeCandidate(value === true) }), t('profileApplicationsQuestionsEditor.candidateToggle')] }), jsxs("div", { className: "flex items-center gap-0.5", children: [jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400", "aria-label": t('profileApplicationsQuestionsEditor.moveUp'), onClick: () => onMove(-1), children: jsx(ArrowUp, { className: "h-3.5 w-3.5" }) }), jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400", "aria-label": t('profileApplicationsQuestionsEditor.moveDown'), onClick: () => onMove(1), children: jsx(ArrowDown, { className: "h-3.5 w-3.5" }) }), jsx(ConfirmPopover, { message: t('profileApplicationsQuestionsEditor.confirmRemoveSection'), onConfirm: onRemove, children: jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400 hover:text-red-600", "aria-label": t('profileApplicationsQuestionsEditor.removeSection'), children: jsx(Trash2, { className: "h-3.5 w-3.5" }) }) })] })] })] }), jsxs(CollapsibleContent, { children: [section.fields.length === 0 ? (jsx("p", { className: "px-5 py-4 text-sm text-gray-400", children: t('profileApplicationsQuestionsEditor.emptySection') })) : (jsx("ul", { className: "divide-y divide-gray-100 dark:divide-gray-800", children: section.fields.map((field, fieldIndex) => (jsxs("li", { className: "flex items-center gap-3 px-5 py-2.5", children: [jsx("span", { className: "min-w-0 flex-1 truncate text-sm text-gray-800 dark:text-gray-200", children: field.label || field.key }), jsx(Badge, { variant: "outline", className: "text-[11px] font-normal text-gray-500", children: field.type }), field.required && (jsx("span", { className: "text-[11px] font-medium text-gray-400", children: t('profileApplicationsQuestionsEditor.required') })), jsxs("div", { className: "flex items-center gap-0.5", children: [jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400", "aria-label": t('profileApplicationsQuestionsEditor.moveUp'), onClick: () => onChange({ ...section, fields: moveItem(section.fields, fieldIndex, -1) }), children: jsx(ArrowUp, { className: "h-3.5 w-3.5" }) }), jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400", "aria-label": t('profileApplicationsQuestionsEditor.moveDown'), onClick: () => onChange({ ...section, fields: moveItem(section.fields, fieldIndex, 1) }), children: jsx(ArrowDown, { className: "h-3.5 w-3.5" }) }), jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400", "aria-label": t('profileApplicationsQuestionsEditor.editQuestion'), onClick: () => onEditField(fieldIndex), children: jsx(Pencil, { className: "h-3.5 w-3.5" }) }), jsx(ConfirmPopover, { message: t('profileApplicationsQuestionsEditor.confirmRemoveQuestion'), onConfirm: () => onChange({
|
|
180321
|
+
...section,
|
|
180322
|
+
fields: section.fields.filter((_, i) => i !== fieldIndex),
|
|
180323
|
+
}), children: jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400 hover:text-red-600", "aria-label": t('profileApplicationsQuestionsEditor.removeQuestion'), children: jsx(Trash2, { className: "h-3.5 w-3.5" }) }) })] })] }, field.key || fieldIndex))) })), jsx("div", { className: "border-t border-gray-100 px-5 py-2.5 dark:border-gray-800", children: jsxs(Button$1, { variant: "ghost", className: "h-8 px-2 text-blue-600 hover:bg-blue-50 hover:text-blue-700 dark:hover:bg-blue-950/40", onClick: onAddField, children: [jsx(Plus, { className: "mr-1 h-4 w-4" }), t('profileApplicationsQuestionsEditor.addQuestion')] }) })] })] }));
|
|
180324
|
+
}
|
|
180325
|
+
/** Small destructive-action confirmation anchored to its trigger. */
|
|
180326
|
+
function ConfirmPopover({ message, onConfirm, children, }) {
|
|
180327
|
+
const t = useT();
|
|
180328
|
+
const [open, setOpen] = useState(false);
|
|
180329
|
+
return (jsxs(Popover, { open: open, onOpenChange: setOpen, children: [jsx(PopoverTrigger, { asChild: true, children: children }), jsxs(PopoverContent, { align: "end", className: "w-60 p-3", portalled: false, children: [jsx("p", { className: "text-sm text-gray-700 dark:text-gray-300", children: message }), jsxs("div", { className: "mt-3 flex justify-end gap-2", children: [jsx(Button$1, { variant: "outline", className: "h-7 px-2.5 text-xs", onClick: () => setOpen(false), children: t('profileApplicationsQuestionsEditor.cancel') }), jsx(Button$1, { className: "h-7 bg-red-600 px-2.5 text-xs text-white hover:bg-red-700", onClick: () => {
|
|
180330
|
+
setOpen(false);
|
|
180331
|
+
onConfirm();
|
|
180332
|
+
}, children: t('profileApplicationsQuestionsEditor.confirmRemove') })] })] })] }));
|
|
180333
|
+
}
|
|
180334
|
+
function FieldDialog({ section, field, onSave, onClose, }) {
|
|
180335
|
+
var _a, _b, _c, _d, _e;
|
|
180336
|
+
const t = useT();
|
|
180337
|
+
const [label, setLabel] = useState((_a = field === null || field === void 0 ? void 0 : field.label) !== null && _a !== void 0 ? _a : '');
|
|
180338
|
+
const [type, setType] = useState((_b = field === null || field === void 0 ? void 0 : field.type) !== null && _b !== void 0 ? _b : 'text');
|
|
180339
|
+
const [required, setRequired] = useState(!!(field === null || field === void 0 ? void 0 : field.required));
|
|
180340
|
+
const [placeholder, setPlaceholder] = useState((_c = field === null || field === void 0 ? void 0 : field.placeholder) !== null && _c !== void 0 ? _c : '');
|
|
180341
|
+
const [helpText, setHelpText] = useState((_d = field === null || field === void 0 ? void 0 : field.help_text) !== null && _d !== void 0 ? _d : '');
|
|
180342
|
+
const [optionsText, setOptionsText] = useState(formatOptionsText(field === null || field === void 0 ? void 0 : field.options));
|
|
180343
|
+
const [mustBeTrue, setMustBeTrue] = useState(!!(field === null || field === void 0 ? void 0 : field.must_be_true));
|
|
180344
|
+
const [content, setContent] = useState((_e = field === null || field === void 0 ? void 0 : field.content) !== null && _e !== void 0 ? _e : '');
|
|
180345
|
+
const handleSave = () => {
|
|
180346
|
+
var _a;
|
|
180347
|
+
const key = (_a = field === null || field === void 0 ? void 0 : field.key) !== null && _a !== void 0 ? _a : slugifyKey(label, section.fields.map((f) => f.key));
|
|
180348
|
+
const next = {
|
|
180349
|
+
// Preserve properties the dialog does not edit (visible_if, validators,
|
|
180350
|
+
// maps_to, file_config, ...).
|
|
180351
|
+
...(field !== null && field !== void 0 ? field : {}),
|
|
180352
|
+
key,
|
|
180353
|
+
type,
|
|
180354
|
+
label: label || undefined,
|
|
180355
|
+
required: required || undefined,
|
|
180356
|
+
placeholder: placeholder || undefined,
|
|
180357
|
+
help_text: helpText || undefined,
|
|
180358
|
+
options: hasOptions(type) ? parseOptionsText(optionsText) : field === null || field === void 0 ? void 0 : field.options,
|
|
180359
|
+
must_be_true: type === 'boolean' ? mustBeTrue || undefined : field === null || field === void 0 ? void 0 : field.must_be_true,
|
|
180360
|
+
content: type === 'info' ? content : field === null || field === void 0 ? void 0 : field.content,
|
|
180361
|
+
};
|
|
180362
|
+
onSave(next);
|
|
180363
|
+
};
|
|
180364
|
+
return (jsx(Dialog, { open: true, onOpenChange: (open) => !open && onClose(), children: jsxs(DialogContent, { className: "max-w-lg", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: field
|
|
180365
|
+
? t('profileApplicationsQuestionsEditor.editQuestion')
|
|
180366
|
+
: t('profileApplicationsQuestionsEditor.addQuestion') }) }), jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "field-label", children: t('profileApplicationsQuestionsEditor.fieldLabelLabel') }), jsx(Input, { id: "field-label", value: label, onChange: (e) => setLabel(e.target.value) })] }), jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { children: t('profileApplicationsQuestionsEditor.fieldTypeLabel') }), jsxs(Select$1, { value: type, onValueChange: (value) => setType(value), children: [jsx(SelectTrigger, { "aria-label": t('profileApplicationsQuestionsEditor.fieldTypeLabel'), children: jsx(SelectValue, {}) }), jsx(SelectContent, { children: EDITABLE_FIELD_TYPES.map((fieldType) => (jsx(SelectItem, { value: fieldType, children: fieldType }, fieldType))) })] })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "field-placeholder", children: t('profileApplicationsQuestionsEditor.fieldPlaceholderLabel') }), jsx(Input, { id: "field-placeholder", value: placeholder, onChange: (e) => setPlaceholder(e.target.value) })] })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "field-help-text", children: t('profileApplicationsQuestionsEditor.fieldHelpTextLabel') }), jsx(Input, { id: "field-help-text", value: helpText, onChange: (e) => setHelpText(e.target.value) })] }), hasOptions(type) && (jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "field-options", children: t('profileApplicationsQuestionsEditor.fieldOptionsLabel') }), jsx(Textarea, { id: "field-options", rows: 4, value: optionsText, onChange: (e) => setOptionsText(e.target.value) })] })), type === 'info' && (jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "field-content", children: t('profileApplicationsQuestionsEditor.fieldContentLabel') }), jsx(Textarea, { id: "field-content", rows: 4, value: content, onChange: (e) => setContent(e.target.value) })] })), jsxs("div", { className: "flex flex-wrap gap-4", children: [type !== 'info' && (jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300", children: [jsx(Checkbox, { checked: required, onCheckedChange: (value) => setRequired(value === true) }), t('profileApplicationsQuestionsEditor.fieldRequiredLabel')] })), type === 'boolean' && (jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300", children: [jsx(Checkbox, { checked: mustBeTrue, onCheckedChange: (value) => setMustBeTrue(value === true) }), t('profileApplicationsQuestionsEditor.fieldMustBeTrueLabel')] }))] }), (field === null || field === void 0 ? void 0 : field.key) && (jsx("p", { className: "text-xs text-gray-400", children: t('profileApplicationsQuestionsEditor.fieldKeyNote', { key: field.key }) }))] }), jsxs(DialogFooter, { children: [jsx(Button$1, { variant: "outline", onClick: onClose, children: t('profileApplicationsQuestionsEditor.cancel') }), jsx(Button$1, { onClick: handleSave, disabled: !label.trim() && type !== 'info', className: "bg-gradient-to-r from-blue-600 to-blue-400 text-white hover:opacity-90", children: t('profileApplicationsQuestionsEditor.saveQuestion') })] })] }) }));
|
|
180367
|
+
}
|
|
180368
|
+
|
|
180369
|
+
// Status vocabulary for the application pipeline: family (application) statuses
|
|
180370
|
+
// and per-student candidate statuses, with badge tones and i18n label keys.
|
|
180371
|
+
/** Family (application) statuses, in pipeline-filter order. */
|
|
180372
|
+
const APPLICATION_STATUSES = [
|
|
180373
|
+
'draft',
|
|
180374
|
+
'submitted',
|
|
180375
|
+
'under_review',
|
|
180376
|
+
'needs_more_info',
|
|
180377
|
+
'interview_required',
|
|
180378
|
+
'waitlisted',
|
|
180379
|
+
'approved',
|
|
180380
|
+
'denied',
|
|
180381
|
+
'withdrawn',
|
|
180382
|
+
];
|
|
180383
|
+
/** Status -> i18n label key (family and candidate vocabularies combined). */
|
|
180384
|
+
const STATUS_LABEL_KEYS = {
|
|
180385
|
+
draft: 'profileApplicationsTab.statusDraft',
|
|
180386
|
+
submitted: 'profileApplicationsTab.statusSubmitted',
|
|
180387
|
+
under_review: 'profileApplicationsTab.statusUnderReview',
|
|
180388
|
+
needs_more_info: 'profileApplicationsTab.statusNeedsMoreInfo',
|
|
180389
|
+
interview_required: 'profileApplicationsTab.statusInterviewRequired',
|
|
180390
|
+
waitlisted: 'profileApplicationsTab.statusWaitlisted',
|
|
180391
|
+
approved: 'profileApplicationsTab.statusApproved',
|
|
180392
|
+
denied: 'profileApplicationsTab.statusDenied',
|
|
180393
|
+
withdrawn: 'profileApplicationsTab.statusWithdrawn',
|
|
180394
|
+
pending: 'profileApplicationsTab.statusPending',
|
|
180395
|
+
accepted: 'profileApplicationsTab.statusAccepted',
|
|
180396
|
+
declined: 'profileApplicationsTab.statusDeclined',
|
|
180397
|
+
};
|
|
180398
|
+
/** Status -> Tailwind tone classes for the Badge. */
|
|
180399
|
+
const STATUS_META = {
|
|
180400
|
+
draft: { className: 'bg-gray-50 text-gray-600 border-gray-200' },
|
|
180401
|
+
submitted: { className: 'bg-blue-50 text-blue-700 border-blue-200' },
|
|
180402
|
+
under_review: { className: 'bg-amber-50 text-amber-700 border-amber-200' },
|
|
180403
|
+
needs_more_info: { className: 'bg-orange-50 text-orange-700 border-orange-200' },
|
|
180404
|
+
interview_required: { className: 'bg-violet-50 text-violet-700 border-violet-200' },
|
|
180405
|
+
waitlisted: { className: 'bg-gray-100 text-gray-600 border-gray-200' },
|
|
180406
|
+
approved: { className: 'bg-emerald-50 text-emerald-700 border-emerald-200' },
|
|
180407
|
+
denied: { className: 'bg-red-50 text-red-700 border-red-200' },
|
|
180408
|
+
withdrawn: { className: 'bg-gray-50 text-gray-500 border-gray-200' },
|
|
180409
|
+
pending: { className: 'bg-gray-50 text-gray-600 border-gray-200' },
|
|
180410
|
+
accepted: { className: 'bg-emerald-50 text-emerald-700 border-emerald-200' },
|
|
180411
|
+
declined: { className: 'bg-red-50 text-red-700 border-red-200' },
|
|
180412
|
+
};
|
|
180413
|
+
const FALLBACK_META = { className: 'bg-gray-50 text-gray-600 border-gray-200' };
|
|
180414
|
+
function statusMeta(status) {
|
|
180415
|
+
return (status && STATUS_META[status]) || FALLBACK_META;
|
|
180416
|
+
}
|
|
180417
|
+
/** Statuses that read as "approved" (family `approved`, candidate `accepted`). */
|
|
180418
|
+
const APPROVED_STATUSES = new Set(['approved', 'accepted']);
|
|
180419
|
+
/**
|
|
180420
|
+
* Two-tone badge classes for the pipeline list: blue once approved/accepted,
|
|
180421
|
+
* neutral grey for every other state. The full {@link STATUS_META} palette is
|
|
180422
|
+
* reserved for the submission workspace, where the exact stage matters.
|
|
180423
|
+
*/
|
|
180424
|
+
function approvalTone(status) {
|
|
180425
|
+
return status && APPROVED_STATUSES.has(status)
|
|
180426
|
+
? 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-300 dark:border-blue-900'
|
|
180427
|
+
: 'bg-gray-100 text-gray-600 border-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600';
|
|
180428
|
+
}
|
|
180429
|
+
/** Applicant display name from the flexible row payload. */
|
|
180430
|
+
function applicantName(applicant) {
|
|
180431
|
+
if (!applicant)
|
|
180432
|
+
return '—';
|
|
180433
|
+
if (typeof applicant === 'string')
|
|
180434
|
+
return applicant;
|
|
180435
|
+
return applicant.name || applicant.username || applicant.email || '—';
|
|
180436
|
+
}
|
|
180437
|
+
/** Reviewer transition actions, in menu order. */
|
|
180438
|
+
const TRANSITION_ACTIONS = [
|
|
180439
|
+
'start_review',
|
|
180440
|
+
'request_info',
|
|
180441
|
+
'require_interview',
|
|
180442
|
+
'waitlist',
|
|
180443
|
+
'accept',
|
|
180444
|
+
'decline',
|
|
180445
|
+
];
|
|
180446
|
+
const TRANSITION_LABEL_KEYS = {
|
|
180447
|
+
start_review: 'profileApplicationsTab.actionStartReview',
|
|
180448
|
+
request_info: 'profileApplicationsTab.actionRequestInfo',
|
|
180449
|
+
require_interview: 'profileApplicationsTab.actionRequireInterview',
|
|
180450
|
+
waitlist: 'profileApplicationsTab.actionWaitlist',
|
|
180451
|
+
accept: 'profileApplicationsTab.actionAccept',
|
|
180452
|
+
decline: 'profileApplicationsTab.actionDecline',
|
|
180453
|
+
};
|
|
180454
|
+
/** Candidate identity states (how the student resolves to an account). */
|
|
180455
|
+
const IDENTITY_LABEL_KEYS = {
|
|
180456
|
+
linked: 'profileApplicationsTab.identityLinked',
|
|
180457
|
+
invited: 'profileApplicationsTab.identityInvited',
|
|
180458
|
+
invite_missing: 'profileApplicationsTab.identityInviteMissing',
|
|
180459
|
+
account_unlinked: 'profileApplicationsTab.identityAccountUnlinked',
|
|
180460
|
+
parent_account: 'profileApplicationsTab.identityParentAccount',
|
|
180461
|
+
};
|
|
180462
|
+
const ENROLLMENT_PHASE_LABEL_KEYS = {
|
|
180463
|
+
enrolled: 'profileApplicationsTab.phaseEnrolled',
|
|
180464
|
+
enrollment_in_progress: 'profileApplicationsTab.phaseEnrollmentInProgress',
|
|
180465
|
+
};
|
|
180466
|
+
/** Placement lifecycle states, in order (not enforced by the API). */
|
|
180467
|
+
const PLACEMENT_STATUSES = [
|
|
180468
|
+
'not_assigned',
|
|
180469
|
+
'assigned',
|
|
180470
|
+
'started',
|
|
180471
|
+
'completed',
|
|
180472
|
+
'reviewed',
|
|
180473
|
+
'confirmed',
|
|
180474
|
+
];
|
|
180475
|
+
/**
|
|
180476
|
+
* Stats payload reader: the endpoint returns `{ counts, post_approval }`;
|
|
180477
|
+
* older backends returned the flat counts object directly.
|
|
180478
|
+
*/
|
|
180479
|
+
function statsCounts(stats) {
|
|
180480
|
+
if (!stats || typeof stats !== 'object')
|
|
180481
|
+
return {};
|
|
180482
|
+
const record = stats;
|
|
180483
|
+
if (record.counts && typeof record.counts === 'object')
|
|
180484
|
+
return record.counts;
|
|
180485
|
+
return Object.fromEntries(Object.entries(stats).filter(([, v]) => typeof v === 'number'));
|
|
180486
|
+
}
|
|
180487
|
+
|
|
180488
|
+
const EDITABLE_STATUSES = new Set(['draft', 'needs_more_info']);
|
|
180489
|
+
function fmtDateTime(value) {
|
|
180490
|
+
if (!value)
|
|
180491
|
+
return '—';
|
|
180492
|
+
const date = new Date(value);
|
|
180493
|
+
return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();
|
|
180494
|
+
}
|
|
180495
|
+
/** One answer value rendered as text (options resolved to their labels). */
|
|
180496
|
+
function displayValue(field, value, t) {
|
|
180497
|
+
if (value == null || value === '')
|
|
180498
|
+
return '—';
|
|
180499
|
+
const optionLabel = (v) => { var _a, _b, _c; return (_c = (_b = (_a = field.options) === null || _a === void 0 ? void 0 : _a.find((o) => o.value === v)) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : String(v); };
|
|
180500
|
+
if (Array.isArray(value))
|
|
180501
|
+
return value.map(optionLabel).join(', ');
|
|
180502
|
+
if (typeof value === 'boolean')
|
|
180503
|
+
return t(value ? 'profileApplicationsTab.answerYes' : 'profileApplicationsTab.answerNo');
|
|
180504
|
+
if (typeof value === 'object') {
|
|
180505
|
+
const sig = value;
|
|
180506
|
+
if (sig.name)
|
|
180507
|
+
return `${sig.name}${sig.signed_at ? ` · ${fmtDateTime(sig.signed_at)}` : ''}`;
|
|
180508
|
+
return JSON.stringify(value);
|
|
180509
|
+
}
|
|
180510
|
+
return optionLabel(value);
|
|
180511
|
+
}
|
|
180512
|
+
/**
|
|
180513
|
+
* The reviewer workspace for one application: per-student decisions and
|
|
180514
|
+
* family-wide transitions, the answers rendered against the schema snapshot,
|
|
180515
|
+
* attachments, the submit-blocking checklist with waivers and the override
|
|
180516
|
+
* submit, placements and course assignments for accepted students, internal
|
|
180517
|
+
* notes, and the audit trail. The fee panel is intentionally not implemented.
|
|
180518
|
+
*/
|
|
180519
|
+
function SubmissionDetail({ platformKey, submissionId, onBack, }) {
|
|
180520
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
180521
|
+
const t = useT();
|
|
180522
|
+
const { data, isLoading, error, refetch } = useGetApplicationAdminDetailQuery({ submission_id: submissionId, platform_key: platformKey }, { refetchOnMountOrArgChange: true });
|
|
180523
|
+
const detail = data;
|
|
180524
|
+
const [transitionTarget, setTransitionTarget] = useState(null);
|
|
180525
|
+
const [linkTarget, setLinkTarget] = useState(null);
|
|
180526
|
+
const [placementDialogOpen, setPlacementDialogOpen] = useState(false);
|
|
180527
|
+
const [assignmentDialogOpen, setAssignmentDialogOpen] = useState(false);
|
|
180528
|
+
const [inviteCandidate, { isLoading: inviting }] = useInviteApplicationCandidateMutation();
|
|
180529
|
+
const [fetchAttachment] = useLazyGetApplicationAttachmentFileQuery();
|
|
180530
|
+
if (isLoading) {
|
|
180531
|
+
return (jsxs("div", { className: "space-y-4 pt-4", children: [jsx(Skeleton, { className: "h-8 w-56" }), jsx(Skeleton, { className: "h-32 w-full" }), jsx(Skeleton, { className: "h-56 w-full" })] }));
|
|
180532
|
+
}
|
|
180533
|
+
if (error || !detail) {
|
|
180534
|
+
return (jsxs("div", { className: "space-y-4 pt-4", children: [jsx(BackButton, { onBack: onBack }), jsx("p", { className: "text-sm text-red-600", children: t('profileApplicationsTab.loadError') })] }));
|
|
180535
|
+
}
|
|
180536
|
+
const candidates = (_a = detail.candidates) !== null && _a !== void 0 ? _a : [];
|
|
180537
|
+
const acceptedCandidates = candidates.filter((c) => c.status === 'accepted');
|
|
180538
|
+
const isEditable = EDITABLE_STATUSES.has((_b = detail.status) !== null && _b !== void 0 ? _b : '');
|
|
180539
|
+
const schemaSections = Array.isArray((_c = detail.form_schema) === null || _c === void 0 ? void 0 : _c.sections)
|
|
180540
|
+
? detail.form_schema.sections
|
|
180541
|
+
: [];
|
|
180542
|
+
const masked = new Set((_d = detail.sensitive_masked) !== null && _d !== void 0 ? _d : []);
|
|
180543
|
+
const candidateLabel = (id) => {
|
|
180544
|
+
var _a;
|
|
180545
|
+
if (id == null)
|
|
180546
|
+
return t('profileApplicationsTab.familyWide');
|
|
180547
|
+
const candidate = candidates.find((c) => c.id === id);
|
|
180548
|
+
return (candidate === null || candidate === void 0 ? void 0 : candidate.label) || ((_a = candidate === null || candidate === void 0 ? void 0 : candidate.details) === null || _a === void 0 ? void 0 : _a.name) || `#${id}`;
|
|
180549
|
+
};
|
|
180550
|
+
const handleDownload = async (attachmentId, filename) => {
|
|
180551
|
+
try {
|
|
180552
|
+
const result = await fetchAttachment({
|
|
180553
|
+
submission_id: submissionId,
|
|
180554
|
+
attachment_id: attachmentId,
|
|
180555
|
+
platform_key: platformKey,
|
|
180556
|
+
}).unwrap();
|
|
180557
|
+
const anchor = document.createElement('a');
|
|
180558
|
+
anchor.href = result.url;
|
|
180559
|
+
anchor.download = filename || `attachment-${attachmentId}`;
|
|
180560
|
+
anchor.click();
|
|
180561
|
+
}
|
|
180562
|
+
catch (_a) {
|
|
180563
|
+
toast.error(t('profileApplicationsTab.downloadFailed'));
|
|
180564
|
+
}
|
|
180565
|
+
};
|
|
180566
|
+
const handleInvite = async (candidate) => {
|
|
180567
|
+
var _a;
|
|
180568
|
+
try {
|
|
180569
|
+
await inviteCandidate({
|
|
180570
|
+
submission_id: submissionId,
|
|
180571
|
+
candidate_id: candidate.id,
|
|
180572
|
+
platform_key: platformKey,
|
|
180573
|
+
}).unwrap();
|
|
180574
|
+
toast.success(t('profileApplicationsTab.inviteSent'));
|
|
180575
|
+
refetch();
|
|
180576
|
+
}
|
|
180577
|
+
catch (err) {
|
|
180578
|
+
toast.error((_a = gateError(err)) !== null && _a !== void 0 ? _a : t('profileApplicationsTab.actionFailed'));
|
|
180579
|
+
}
|
|
180580
|
+
};
|
|
180581
|
+
return (jsxs("div", { className: "space-y-5 pt-4", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [jsx(BackButton, { onBack: onBack }), jsx(Button$1, { className: "bg-gradient-to-r from-blue-600 to-blue-400 text-white hover:opacity-90", onClick: () => setTransitionTarget({ candidate: null }), "aria-label": t('profileApplicationsTab.decideAll'), children: t('profileApplicationsTab.decide') })] }), jsx("div", { className: "rounded-xl border border-gray-200 bg-white p-5 shadow-sm dark:border-gray-700 dark:bg-gray-900", children: jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [jsxs("div", { children: [jsx("h4", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: ((_e = detail.user) === null || _e === void 0 ? void 0 : _e.name) || ((_f = detail.user) === null || _f === void 0 ? void 0 : _f.username) || ((_g = detail.user) === null || _g === void 0 ? void 0 : _g.email) || '—' }), jsx("p", { className: "text-sm text-gray-500", children: (_h = detail.user) === null || _h === void 0 ? void 0 : _h.email })] }), jsxs("div", { className: "text-right", children: [jsx(Badge, { variant: "outline", className: statusMeta(detail.status).className, children: STATUS_LABEL_KEYS[(_j = detail.status) !== null && _j !== void 0 ? _j : '']
|
|
180582
|
+
? t(STATUS_LABEL_KEYS[(_k = detail.status) !== null && _k !== void 0 ? _k : ''])
|
|
180583
|
+
: detail.status }), jsx("p", { className: "mt-1 text-xs text-gray-400", children: t('profileApplicationsTab.submittedAt', {
|
|
180584
|
+
date: fmtDateTime(detail.submitted_at),
|
|
180585
|
+
}) })] })] }) }), jsxs("section", { className: "space-y-3", children: [jsxs("h5", { className: "flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-gray-100", children: [jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-400", children: jsx(Users, { className: "h-3.5 w-3.5" }) }), t('profileApplicationsTab.studentsHeading')] }), candidates.length === 0 ? (jsx("p", { className: "text-sm text-gray-400", children: t('profileApplicationsTab.noStudentsYet') })) : (jsx("div", { className: "grid gap-2", children: candidates.map((candidate) => {
|
|
180586
|
+
var _a, _b, _c;
|
|
180587
|
+
return (jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-xl border border-gray-200 bg-white px-4 py-3 shadow-sm dark:border-gray-700 dark:bg-gray-900", children: [jsxs("div", { className: "min-w-0", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "font-medium text-gray-900 dark:text-gray-100", children: candidate.label || ((_a = candidate.details) === null || _a === void 0 ? void 0 : _a.name) || `#${candidate.id}` }), jsx(Badge, { variant: "outline", className: statusMeta(candidate.status).className, children: STATUS_LABEL_KEYS[(_b = candidate.status) !== null && _b !== void 0 ? _b : '']
|
|
180588
|
+
? t(STATUS_LABEL_KEYS[(_c = candidate.status) !== null && _c !== void 0 ? _c : ''])
|
|
180589
|
+
: candidate.status }), candidate.identity && IDENTITY_LABEL_KEYS[candidate.identity] && (jsx(Badge, { variant: "outline", className: "text-[11px] text-gray-500", children: t(IDENTITY_LABEL_KEYS[candidate.identity]) })), candidate.enrollment_phase &&
|
|
180590
|
+
ENROLLMENT_PHASE_LABEL_KEYS[candidate.enrollment_phase] && (jsx(Badge, { variant: "outline", className: "text-[11px] text-blue-600", children: t(ENROLLMENT_PHASE_LABEL_KEYS[candidate.enrollment_phase]) }))] }), candidate.decision_message && (jsx("p", { className: "mt-1 text-xs text-gray-500", children: candidate.decision_message }))] }), jsxs("div", { className: "flex items-center gap-1.5", children: [candidate.status === 'accepted' && candidate.identity === 'invite_missing' && (jsxs(Button$1, { variant: "outline", className: "h-8 px-2.5 text-xs", disabled: inviting, onClick: () => handleInvite(candidate), children: [jsx(Mail, { className: "mr-1 h-3.5 w-3.5" }), t('profileApplicationsTab.sendInvite')] })), candidate.status === 'accepted' && candidate.identity === 'account_unlinked' && (jsxs(Button$1, { variant: "outline", className: "h-8 px-2.5 text-xs", onClick: () => setLinkTarget(candidate), children: [jsx(Link2, { className: "mr-1 h-3.5 w-3.5" }), t('profileApplicationsTab.linkAccount')] })), jsx(Button$1, { variant: "outline", className: "h-8 px-2.5 text-xs", onClick: () => setTransitionTarget({ candidate }), children: t('profileApplicationsTab.decide') })] })] }, candidate.id));
|
|
180591
|
+
}) }))] }), isEditable && (jsx(BlockingChecklist, { platformKey: platformKey, submissionId: submissionId, detail: detail, onChanged: refetch })), jsxs("section", { className: "space-y-3", children: [jsxs("h5", { className: "flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-gray-100", children: [jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-400", children: jsx(FileText, { className: "h-3.5 w-3.5" }) }), t('profileApplicationsTab.answersHeading')] }), schemaSections.map((section) => {
|
|
180592
|
+
var _a, _b;
|
|
180593
|
+
if (masked.has(section.key)) {
|
|
180594
|
+
return jsx(MaskedNotice, { title: section.title || section.key }, section.key);
|
|
180595
|
+
}
|
|
180596
|
+
const answers = (_a = detail.responses) === null || _a === void 0 ? void 0 : _a[section.key];
|
|
180597
|
+
const entries = section.repeatable
|
|
180598
|
+
? Array.isArray(answers)
|
|
180599
|
+
? answers
|
|
180600
|
+
: []
|
|
180601
|
+
: [((_b = answers) !== null && _b !== void 0 ? _b : {})];
|
|
180602
|
+
return (jsxs("div", { className: "rounded-xl border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-900", children: [jsx("p", { className: "border-b border-gray-100 px-4 py-2 text-xs font-semibold uppercase tracking-wide text-gray-500 dark:border-gray-800", children: section.title || section.key }), jsx("div", { className: "divide-y divide-gray-50 dark:divide-gray-800", children: entries.map((entry, index) => {
|
|
180603
|
+
var _a, _b;
|
|
180604
|
+
return (jsxs("div", { className: "px-4 py-2.5", children: [section.repeatable && (jsx("p", { className: "mb-1 text-xs font-medium text-gray-400", children: t('profileApplicationsTab.entryN', { n: index + 1 }) })), jsx("dl", { className: "grid gap-x-6 gap-y-1.5 sm:grid-cols-2", children: ((_a = section.fields) !== null && _a !== void 0 ? _a : [])
|
|
180605
|
+
.filter((field) => field.type !== 'info')
|
|
180606
|
+
.map((field) => (jsxs("div", { className: "flex items-baseline gap-2", children: [jsx("dt", { className: "shrink-0 text-xs text-gray-500", children: field.label || field.key }), jsx("dd", { className: "min-w-0 break-words text-sm text-gray-800 dark:text-gray-200", children: masked.has(`${section.key}.${field.key}`) ? (jsxs("span", { className: "inline-flex items-center gap-1 text-gray-400", children: [jsx(EyeOff, { className: "h-3 w-3" }), t('profileApplicationsTab.maskedValue')] })) : (displayValue(field, entry[field.key], t)) })] }, field.key))) })] }, (_b = entry._id) !== null && _b !== void 0 ? _b : index));
|
|
180607
|
+
}) })] }, section.key));
|
|
180608
|
+
})] }), ((_l = detail.attachments) !== null && _l !== void 0 ? _l : []).length > 0 && (jsxs("section", { className: "space-y-3", children: [jsxs("h5", { className: "flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-gray-100", children: [jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-400", children: jsx(Paperclip, { className: "h-3.5 w-3.5" }) }), t('profileApplicationsTab.attachmentsHeading')] }), jsx("div", { className: "flex flex-wrap gap-2", children: ((_m = detail.attachments) !== null && _m !== void 0 ? _m : []).map((attachment) => (jsxs("button", { type: "button", onClick: () => handleDownload(attachment.id, attachment.filename), className: "inline-flex items-center gap-1.5 rounded-md border border-gray-200 bg-white px-2.5 py-1.5 text-xs text-gray-700 shadow-sm hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200 dark:hover:bg-gray-800", children: [jsx(Download, { className: "h-3.5 w-3.5" }), attachment.filename || `#${attachment.id}`] }, attachment.id))) })] })), acceptedCandidates.length > 0 && (jsxs(Fragment$1, { children: [jsx(PlacementsSection, { platformKey: platformKey, submissionId: submissionId, detail: detail, candidateLabel: candidateLabel, onAdd: () => setPlacementDialogOpen(true) }), jsx(AssignmentsSection, { platformKey: platformKey, submissionId: submissionId, detail: detail, candidateLabel: candidateLabel, onAdd: () => setAssignmentDialogOpen(true), onChanged: refetch })] })), jsx(NotesSection, { platformKey: platformKey, submissionId: submissionId, detail: detail }), jsxs("section", { className: "space-y-3", children: [jsxs("h5", { className: "flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-gray-100", children: [jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-400", children: jsx(History, { className: "h-3.5 w-3.5" }) }), t('profileApplicationsTab.auditHeading')] }), ((_o = detail.status_events) !== null && _o !== void 0 ? _o : []).length === 0 ? (jsx("p", { className: "text-sm text-gray-400", children: t('profileApplicationsTab.noEvents') })) : (jsx("ul", { className: "space-y-1.5", children: ((_p = detail.status_events) !== null && _p !== void 0 ? _p : []).map((event) => {
|
|
180609
|
+
var _a;
|
|
180610
|
+
return (jsxs("li", { className: "text-xs text-gray-500", children: [jsx("span", { className: "text-gray-400", children: fmtDateTime(event.created) }), ' ', jsx("span", { className: "font-medium text-gray-700 dark:text-gray-300", children: event.actor || '—' }), ' ', "\u00B7 ", candidateLabel(event.candidate_id), " \u00B7", ' ', event.from_status
|
|
180611
|
+
? `${event.from_status} → ${event.to_status}`
|
|
180612
|
+
: ((_a = event.to_status) !== null && _a !== void 0 ? _a : ''), event.note ? ` — ${event.note}` : ''] }, event.id));
|
|
180613
|
+
}) }))] }), transitionTarget && (jsx(TransitionDialog, { platformKey: platformKey, submissionId: submissionId, candidate: transitionTarget.candidate, onClose: () => setTransitionTarget(null), onDone: () => {
|
|
180614
|
+
setTransitionTarget(null);
|
|
180615
|
+
refetch();
|
|
180616
|
+
} })), linkTarget && (jsx(LinkCandidateDialog, { platformKey: platformKey, submissionId: submissionId, candidate: linkTarget, onClose: () => setLinkTarget(null), onDone: () => {
|
|
180617
|
+
setLinkTarget(null);
|
|
180618
|
+
refetch();
|
|
180619
|
+
} })), placementDialogOpen && (jsx(PlacementDialog, { platformKey: platformKey, submissionId: submissionId, candidates: acceptedCandidates, onClose: () => setPlacementDialogOpen(false), onDone: () => {
|
|
180620
|
+
setPlacementDialogOpen(false);
|
|
180621
|
+
refetch();
|
|
180622
|
+
} })), assignmentDialogOpen && (jsx(AssignmentDialog, { platformKey: platformKey, submissionId: submissionId, candidates: acceptedCandidates, onClose: () => setAssignmentDialogOpen(false), onDone: () => {
|
|
180623
|
+
setAssignmentDialogOpen(false);
|
|
180624
|
+
refetch();
|
|
180625
|
+
} }))] }));
|
|
180626
|
+
}
|
|
180627
|
+
function BackButton({ onBack }) {
|
|
180628
|
+
const t = useT();
|
|
180629
|
+
return (jsxs("button", { type: "button", onClick: onBack, className: "inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 dark:text-gray-300", children: [jsx(ArrowLeft, { className: "h-4 w-4" }), t('profileApplicationsTab.backToSubmissions')] }));
|
|
180630
|
+
}
|
|
180631
|
+
function MaskedNotice({ title }) {
|
|
180632
|
+
const t = useT();
|
|
180633
|
+
return (jsxs("div", { className: "flex items-center gap-2 rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-500 dark:border-gray-700 dark:bg-gray-800/50", children: [jsx(EyeOff, { className: "h-4 w-4" }), t('profileApplicationsTab.sectionMasked', { section: title })] }));
|
|
180634
|
+
}
|
|
180635
|
+
/** Extract the stable machine code from a gate error payload, if any. */
|
|
180636
|
+
function gateError(err) {
|
|
180637
|
+
var _a;
|
|
180638
|
+
const code = (_a = err === null || err === void 0 ? void 0 : err.data) === null || _a === void 0 ? void 0 : _a.error;
|
|
180639
|
+
return typeof code === 'string' ? code : null;
|
|
180640
|
+
}
|
|
180641
|
+
/** Decide/move dialog, per-candidate or family-wide. */
|
|
180642
|
+
function TransitionDialog({ platformKey, submissionId, candidate, onClose, onDone, }) {
|
|
180643
|
+
var _a;
|
|
180644
|
+
const t = useT();
|
|
180645
|
+
const [action, setAction] = useState('start_review');
|
|
180646
|
+
const [message, setMessage] = useState('');
|
|
180647
|
+
const [note, setNote] = useState('');
|
|
180648
|
+
const [block, setBlock] = useState(false);
|
|
180649
|
+
const [transition, { isLoading }] = useTransitionApplicationMutation();
|
|
180650
|
+
const handleApply = async () => {
|
|
180651
|
+
try {
|
|
180652
|
+
await transition({
|
|
180653
|
+
submission_id: submissionId,
|
|
180654
|
+
platform_key: platformKey,
|
|
180655
|
+
action,
|
|
180656
|
+
...(candidate ? { candidate_id: candidate.id } : {}),
|
|
180657
|
+
...(message.trim() ? { message: message.trim() } : {}),
|
|
180658
|
+
...(note.trim() ? { note: note.trim() } : {}),
|
|
180659
|
+
...(action === 'decline' && block ? { block: true } : {}),
|
|
180660
|
+
}).unwrap();
|
|
180661
|
+
toast.success(t('profileApplicationsTab.transitionApplied'));
|
|
180662
|
+
onDone();
|
|
180663
|
+
}
|
|
180664
|
+
catch (err) {
|
|
180665
|
+
const code = gateError(err);
|
|
180666
|
+
toast.error(code === 'illegal_transition'
|
|
180667
|
+
? t('profileApplicationsTab.illegalTransition')
|
|
180668
|
+
: code === 'no_candidates'
|
|
180669
|
+
? t('profileApplicationsTab.noCandidatesYet')
|
|
180670
|
+
: t('profileApplicationsTab.actionFailed'));
|
|
180671
|
+
}
|
|
180672
|
+
};
|
|
180673
|
+
return (jsx(Dialog, { open: true, onOpenChange: (open) => !open && onClose(), children: jsxs(DialogContent, { className: "max-w-md", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: candidate
|
|
180674
|
+
? t('profileApplicationsTab.decideStudentTitle', {
|
|
180675
|
+
name: candidate.label || ((_a = candidate.details) === null || _a === void 0 ? void 0 : _a.name) || `#${candidate.id}`,
|
|
180676
|
+
})
|
|
180677
|
+
: t('profileApplicationsTab.decideFamilyTitle') }) }), jsxs("div", { className: "space-y-3", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { children: t('profileApplicationsTab.actionLabel') }), jsxs(Select$1, { value: action, onValueChange: (value) => setAction(value), children: [jsx(SelectTrigger, { "aria-label": t('profileApplicationsTab.actionLabel'), children: jsx(SelectValue, {}) }), jsx(SelectContent, { children: TRANSITION_ACTIONS.map((key) => (jsx(SelectItem, { value: key, children: t(TRANSITION_LABEL_KEYS[key]) }, key))) })] })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "transition-message", children: t('profileApplicationsTab.messageLabel') }), jsx(Textarea, { id: "transition-message", rows: 3, value: message, onChange: (e) => setMessage(e.target.value), placeholder: t('profileApplicationsTab.messagePlaceholder') })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "transition-note", children: t('profileApplicationsTab.internalNoteLabel') }), jsx(Textarea, { id: "transition-note", rows: 3, value: note, onChange: (e) => setNote(e.target.value) })] }), action === 'decline' && (jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300", children: [jsx(Checkbox, { checked: block, onCheckedChange: (value) => setBlock(value === true) }), t('profileApplicationsTab.blockOnDecline')] }))] }), jsxs(DialogFooter, { children: [jsx(Button$1, { variant: "outline", onClick: onClose, children: t('profileApplicationsTab.cancel') }), jsx(Button$1, { className: "bg-gradient-to-r from-blue-600 to-blue-400 text-white hover:opacity-90", onClick: handleApply, disabled: isLoading, children: t('profileApplicationsTab.applyAction') })] })] }) }));
|
|
180678
|
+
}
|
|
180679
|
+
/** Link an accepted student to an existing platform account (identity: account_unlinked). */
|
|
180680
|
+
function LinkCandidateDialog({ platformKey, submissionId, candidate, onClose, onDone, }) {
|
|
180681
|
+
var _a;
|
|
180682
|
+
const t = useT();
|
|
180683
|
+
const [email, setEmail] = useState('');
|
|
180684
|
+
const [link, { isLoading }] = useLinkApplicationCandidateMutation();
|
|
180685
|
+
const handleLink = async () => {
|
|
180686
|
+
try {
|
|
180687
|
+
await link({
|
|
180688
|
+
submission_id: submissionId,
|
|
180689
|
+
candidate_id: candidate.id,
|
|
180690
|
+
platform_key: platformKey,
|
|
180691
|
+
email: email.trim(),
|
|
180692
|
+
}).unwrap();
|
|
180693
|
+
toast.success(t('profileApplicationsTab.accountLinked'));
|
|
180694
|
+
onDone();
|
|
180695
|
+
}
|
|
180696
|
+
catch (err) {
|
|
180697
|
+
const code = gateError(err);
|
|
180698
|
+
toast.error(code === 'user_not_found'
|
|
180699
|
+
? t('profileApplicationsTab.userNotFound')
|
|
180700
|
+
: t('profileApplicationsTab.actionFailed'));
|
|
180701
|
+
}
|
|
180702
|
+
};
|
|
180703
|
+
return (jsx(Dialog, { open: true, onOpenChange: (open) => !open && onClose(), children: jsxs(DialogContent, { className: "max-w-sm", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: t('profileApplicationsTab.linkAccountTitle', {
|
|
180704
|
+
name: candidate.label || ((_a = candidate.details) === null || _a === void 0 ? void 0 : _a.name) || `#${candidate.id}`,
|
|
180705
|
+
}) }) }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "link-email", children: t('profileApplicationsTab.accountEmailLabel') }), jsx(Input, { id: "link-email", type: "email", value: email, onChange: (e) => setEmail(e.target.value) })] }), jsxs(DialogFooter, { children: [jsx(Button$1, { variant: "outline", onClick: onClose, children: t('profileApplicationsTab.cancel') }), jsx(Button$1, { className: "bg-gradient-to-r from-blue-600 to-blue-400 text-white hover:opacity-90", onClick: handleLink, disabled: isLoading || !email.trim(), children: t('profileApplicationsTab.linkAccount') })] })] }) }));
|
|
180706
|
+
}
|
|
180707
|
+
/** The submit-blocking checklist with per-item waive/unwaive and the override submit. */
|
|
180708
|
+
function BlockingChecklist({ platformKey, submissionId, detail, onChanged, }) {
|
|
180709
|
+
var _a;
|
|
180710
|
+
const t = useT();
|
|
180711
|
+
const [createWaiver] = useCreateApplicationWaiverMutation();
|
|
180712
|
+
const [deleteWaiver] = useDeleteApplicationWaiverMutation();
|
|
180713
|
+
const [overrideSubmit, { isLoading: overriding }] = useSubmitApplicationOverrideMutation();
|
|
180714
|
+
const [confirmOverride, setConfirmOverride] = useState(false);
|
|
180715
|
+
const [overrideNote, setOverrideNote] = useState('');
|
|
180716
|
+
const items = (_a = detail.blocking_items) !== null && _a !== void 0 ? _a : [];
|
|
180717
|
+
const toggleWaiver = async (item) => {
|
|
180718
|
+
if (!item.section || !item.field)
|
|
180719
|
+
return;
|
|
180720
|
+
try {
|
|
180721
|
+
const args = {
|
|
180722
|
+
submission_id: submissionId,
|
|
180723
|
+
platform_key: platformKey,
|
|
180724
|
+
section: item.section,
|
|
180725
|
+
field: item.field,
|
|
180726
|
+
};
|
|
180727
|
+
if (item.waived)
|
|
180728
|
+
await deleteWaiver(args).unwrap();
|
|
180729
|
+
else
|
|
180730
|
+
await createWaiver(args).unwrap();
|
|
180731
|
+
onChanged();
|
|
180732
|
+
}
|
|
180733
|
+
catch (_a) {
|
|
180734
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
180735
|
+
}
|
|
180736
|
+
};
|
|
180737
|
+
const handleOverride = async () => {
|
|
180738
|
+
try {
|
|
180739
|
+
await overrideSubmit({
|
|
180740
|
+
submission_id: submissionId,
|
|
180741
|
+
platform_key: platformKey,
|
|
180742
|
+
...(overrideNote.trim() ? { note: overrideNote.trim() } : {}),
|
|
180743
|
+
}).unwrap();
|
|
180744
|
+
toast.success(t('profileApplicationsTab.overrideSubmitted'));
|
|
180745
|
+
setConfirmOverride(false);
|
|
180746
|
+
onChanged();
|
|
180747
|
+
}
|
|
180748
|
+
catch (_a) {
|
|
180749
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
180750
|
+
}
|
|
180751
|
+
};
|
|
180752
|
+
return (jsxs("section", { className: "space-y-3 rounded-xl border border-amber-200 bg-amber-50/40 p-5 dark:border-amber-900 dark:bg-amber-950/20", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [jsxs("h5", { className: "inline-flex items-center gap-1.5 text-sm font-semibold text-gray-900 dark:text-gray-100", children: [jsx(ShieldCheck, { className: "h-4 w-4 text-amber-600" }), t('profileApplicationsTab.blockingHeading')] }), jsx(Button$1, { variant: "outline", className: "h-8 px-3 text-xs", onClick: () => setConfirmOverride(true), disabled: overriding, children: t('profileApplicationsTab.overrideSubmit') })] }), items.length === 0 ? (jsx("p", { className: "text-sm text-gray-500", children: t('profileApplicationsTab.noBlockingItems') })) : (jsx("ul", { className: "space-y-1.5", children: items.map((item, i) => (jsxs("li", { className: "flex items-center justify-between gap-3 text-sm", children: [jsxs("span", { className: item.waived ? 'text-gray-400 line-through' : 'text-gray-700 dark:text-gray-300', children: [[item.section, item.field].filter(Boolean).join('.'), item.index != null ? ` [${item.index}]` : '', " \u2014 ", item.error] }), item.code === 'required' && item.section && item.field && (jsx(Button$1, { variant: "ghost", className: "h-7 px-2 text-xs text-blue-600", onClick: () => toggleWaiver(item), children: t(item.waived
|
|
180753
|
+
? 'profileApplicationsTab.reinstate'
|
|
180754
|
+
: 'profileApplicationsTab.waive') }))] }, i))) })), confirmOverride && (jsx(Dialog, { open: true, onOpenChange: (open) => !open && setConfirmOverride(false), children: jsxs(DialogContent, { className: "max-w-sm", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: t('profileApplicationsTab.overrideSubmitTitle') }) }), jsx("p", { className: "text-sm text-gray-600 dark:text-gray-300", children: t('profileApplicationsTab.overrideSubmitBody') }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "override-note", children: t('profileApplicationsTab.internalNoteLabel') }), jsx(Input, { id: "override-note", value: overrideNote, onChange: (e) => setOverrideNote(e.target.value) })] }), jsxs(DialogFooter, { children: [jsx(Button$1, { variant: "outline", onClick: () => setConfirmOverride(false), children: t('profileApplicationsTab.cancel') }), jsx(Button$1, { className: "bg-gradient-to-r from-blue-600 to-blue-400 text-white hover:opacity-90", onClick: handleOverride, disabled: overriding, children: t('profileApplicationsTab.overrideSubmit') })] })] }) }))] }));
|
|
180755
|
+
}
|
|
180756
|
+
/** Placement tests for accepted students. */
|
|
180757
|
+
function PlacementsSection({ platformKey, submissionId, detail, candidateLabel, onAdd, }) {
|
|
180758
|
+
var _a;
|
|
180759
|
+
const t = useT();
|
|
180760
|
+
const [updatePlacement] = useUpdateApplicationPlacementMutation();
|
|
180761
|
+
const placements = (_a = detail.placements) !== null && _a !== void 0 ? _a : [];
|
|
180762
|
+
const handleStatusChange = async (placementId, status) => {
|
|
180763
|
+
try {
|
|
180764
|
+
await updatePlacement({
|
|
180765
|
+
submission_id: submissionId,
|
|
180766
|
+
placement_id: placementId,
|
|
180767
|
+
platform_key: platformKey,
|
|
180768
|
+
status,
|
|
180769
|
+
}).unwrap();
|
|
180770
|
+
toast.success(t('profileApplicationsTab.placementUpdated'));
|
|
180771
|
+
}
|
|
180772
|
+
catch (_a) {
|
|
180773
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
180774
|
+
}
|
|
180775
|
+
};
|
|
180776
|
+
return (jsxs("section", { className: "space-y-3", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs("h5", { className: "flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-gray-100", children: [jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-400", children: jsx(ClipboardList, { className: "h-3.5 w-3.5" }) }), t('profileApplicationsTab.placementsHeading')] }), jsxs(Button$1, { variant: "outline", className: "h-8 px-3 text-xs", onClick: onAdd, children: [jsx(Plus, { className: "mr-1 h-3.5 w-3.5" }), t('profileApplicationsTab.assignPlacement')] })] }), placements.length === 0 ? (jsx("p", { className: "text-sm text-gray-400", children: t('profileApplicationsTab.noPlacements') })) : (jsx("div", { className: "grid gap-2", children: placements.map((placement) => {
|
|
180777
|
+
var _a;
|
|
180778
|
+
return (jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-xl border border-gray-200 bg-white px-4 py-2.5 text-sm shadow-sm dark:border-gray-700 dark:bg-gray-900", children: [jsxs("div", { className: "min-w-0", children: [jsx("span", { className: "font-medium text-gray-900 dark:text-gray-100", children: candidateLabel(placement.candidate_id) }), jsxs("span", { className: "text-gray-500", children: [placement.label ? ` · ${placement.label}` : '', placement.mentor_label ? ` · ${placement.mentor_label}` : ''] }), placement.result != null && (jsxs("p", { className: "mt-0.5 truncate text-xs text-gray-400", children: [t('profileApplicationsTab.placementResult'), ":", ' ', typeof placement.result === 'string'
|
|
180779
|
+
? placement.result
|
|
180780
|
+
: JSON.stringify(placement.result)] }))] }), jsxs(Select$1, { value: (_a = placement.status) !== null && _a !== void 0 ? _a : 'not_assigned', onValueChange: (value) => handleStatusChange(placement.id, value), children: [jsx(SelectTrigger, { className: "h-8 w-40 text-xs", "aria-label": t('profileApplicationsTab.placementStatusLabel'), children: jsx(SelectValue, {}) }), jsx(SelectContent, { children: PLACEMENT_STATUSES.map((key) => (jsx(SelectItem, { value: key, children: t(`profileApplicationsTab.placement_${key}`) }, key))) })] })] }, placement.id));
|
|
180781
|
+
}) }))] }));
|
|
180782
|
+
}
|
|
180783
|
+
const MENTORS_PER_PAGE = 20;
|
|
180784
|
+
function PlacementDialog({ platformKey, submissionId, candidates, onClose, onDone, }) {
|
|
180785
|
+
const t = useT();
|
|
180786
|
+
const [candidateId, setCandidateId] = useState(candidates[0] ? String(candidates[0].id) : '');
|
|
180787
|
+
const [label, setLabel] = useState('');
|
|
180788
|
+
const [createPlacements, { isLoading }] = useCreateApplicationPlacementsMutation();
|
|
180789
|
+
// Agent picker: styled like the course picker in AssignmentDialog (debounced
|
|
180790
|
+
// search, page accumulation with scroll-to-load-more) but single-select —
|
|
180791
|
+
// picking an agent closes the dropdown, and its name becomes mentor_label.
|
|
180792
|
+
const [mentor, setMentor] = useState(null);
|
|
180793
|
+
const [mentorSearchTerm, setMentorSearchTerm] = useState('');
|
|
180794
|
+
const [debouncedMentorSearch, setDebouncedMentorSearch] = useState('');
|
|
180795
|
+
const [isMentorDropdownOpen, setIsMentorDropdownOpen] = useState(false);
|
|
180796
|
+
const [mentorCurrentPage, setMentorCurrentPage] = useState(1);
|
|
180797
|
+
const [accumulatedMentors, setAccumulatedMentors] = useState([]);
|
|
180798
|
+
const mentorDropdownRef = useRef(null);
|
|
180799
|
+
const mentorScrollRef = useRef(null);
|
|
180800
|
+
useEffect(() => {
|
|
180801
|
+
const handler = setTimeout(() => setDebouncedMentorSearch(mentorSearchTerm), 300);
|
|
180802
|
+
return () => clearTimeout(handler);
|
|
180803
|
+
}, [mentorSearchTerm]);
|
|
180804
|
+
const { data: mentorsData, isLoading: isLoadingMentors, isFetching: isFetchingMentors, } = useGetCustomMentorsQuery({
|
|
180805
|
+
tenantKey: platformKey,
|
|
180806
|
+
username: getUserName$1(),
|
|
180807
|
+
params: {
|
|
180808
|
+
query: debouncedMentorSearch || '',
|
|
180809
|
+
limit: MENTORS_PER_PAGE,
|
|
180810
|
+
offset: (mentorCurrentPage - 1) * MENTORS_PER_PAGE,
|
|
180811
|
+
},
|
|
180812
|
+
}, { skip: !isMentorDropdownOpen });
|
|
180813
|
+
// New search restarts paging.
|
|
180814
|
+
useEffect(() => {
|
|
180815
|
+
setMentorCurrentPage(1);
|
|
180816
|
+
setAccumulatedMentors([]);
|
|
180817
|
+
}, [debouncedMentorSearch]);
|
|
180818
|
+
// Accumulate agents across pages (deduplicated by unique_id).
|
|
180819
|
+
useEffect(() => {
|
|
180820
|
+
const results = mentorsData === null || mentorsData === void 0 ? void 0 : mentorsData.results;
|
|
180821
|
+
if (!results)
|
|
180822
|
+
return;
|
|
180823
|
+
if (mentorCurrentPage === 1) {
|
|
180824
|
+
setAccumulatedMentors(results);
|
|
180825
|
+
}
|
|
180826
|
+
else {
|
|
180827
|
+
setAccumulatedMentors((prev) => {
|
|
180828
|
+
const existing = new Set(prev.map((m) => m.unique_id));
|
|
180829
|
+
return [...prev, ...results.filter((m) => !existing.has(m.unique_id))];
|
|
180830
|
+
});
|
|
180831
|
+
}
|
|
180832
|
+
}, [mentorsData, mentorCurrentPage]);
|
|
180833
|
+
const hasMoreMentors = Boolean(mentorsData === null || mentorsData === void 0 ? void 0 : mentorsData.next);
|
|
180834
|
+
const handleMentorScroll = () => {
|
|
180835
|
+
const scrollContainer = mentorScrollRef.current;
|
|
180836
|
+
if (!scrollContainer || isFetchingMentors || !hasMoreMentors)
|
|
180837
|
+
return;
|
|
180838
|
+
const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
|
|
180839
|
+
if (scrollHeight - scrollTop - clientHeight < 50) {
|
|
180840
|
+
setMentorCurrentPage((prev) => prev + 1);
|
|
180841
|
+
}
|
|
180842
|
+
};
|
|
180843
|
+
// Close the picker on outside clicks.
|
|
180844
|
+
useEffect(() => {
|
|
180845
|
+
const handleClickOutside = (event) => {
|
|
180846
|
+
if (mentorDropdownRef.current && !mentorDropdownRef.current.contains(event.target)) {
|
|
180847
|
+
setIsMentorDropdownOpen(false);
|
|
180848
|
+
}
|
|
180849
|
+
};
|
|
180850
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
180851
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
180852
|
+
}, []);
|
|
180853
|
+
const handleCreate = async () => {
|
|
180854
|
+
var _a, _b;
|
|
180855
|
+
if (!mentor)
|
|
180856
|
+
return;
|
|
180857
|
+
try {
|
|
180858
|
+
const result = (await createPlacements({
|
|
180859
|
+
submission_id: submissionId,
|
|
180860
|
+
platform_key: platformKey,
|
|
180861
|
+
placements: [
|
|
180862
|
+
{
|
|
180863
|
+
candidate_id: Number(candidateId),
|
|
180864
|
+
mentor_unique_id: mentor.unique_id,
|
|
180865
|
+
...(mentor.name ? { mentor_label: mentor.name } : {}),
|
|
180866
|
+
...(label.trim() ? { label: label.trim() } : {}),
|
|
180867
|
+
},
|
|
180868
|
+
],
|
|
180869
|
+
}).unwrap());
|
|
180870
|
+
const itemError = (_b = (_a = result.results) === null || _a === void 0 ? void 0 : _a.find((row) => row.error)) === null || _b === void 0 ? void 0 : _b.error;
|
|
180871
|
+
if (itemError) {
|
|
180872
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
180873
|
+
return;
|
|
180874
|
+
}
|
|
180875
|
+
toast.success(t('profileApplicationsTab.placementAssigned'));
|
|
180876
|
+
onDone();
|
|
180877
|
+
}
|
|
180878
|
+
catch (_c) {
|
|
180879
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
180880
|
+
}
|
|
180881
|
+
};
|
|
180882
|
+
return (jsx(Dialog, { open: true, onOpenChange: (open) => !open && onClose(), children: jsxs(DialogContent, { className: "max-w-md", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: t('profileApplicationsTab.assignPlacement') }) }), jsxs("div", { className: "space-y-3", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { children: t('profileApplicationsTab.studentLabel') }), jsxs(Select$1, { value: candidateId, onValueChange: setCandidateId, children: [jsx(SelectTrigger, { "aria-label": t('profileApplicationsTab.studentLabel'), children: jsx(SelectValue, {}) }), jsx(SelectContent, { children: candidates.map((candidate) => {
|
|
180883
|
+
var _a;
|
|
180884
|
+
return (jsx(SelectItem, { value: String(candidate.id), children: candidate.label || ((_a = candidate.details) === null || _a === void 0 ? void 0 : _a.name) || `#${candidate.id}` }, candidate.id));
|
|
180885
|
+
}) })] })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { children: t('profileApplicationsTab.placementAgentLabel') }), jsxs("div", { className: "relative", ref: mentorDropdownRef, children: [jsxs(Button$1, { type: "button", variant: "outline", className: "w-full justify-between border-gray-300 bg-white hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:hover:bg-gray-800", onClick: () => setIsMentorDropdownOpen((open) => !open), "aria-expanded": isMentorDropdownOpen, "aria-haspopup": "listbox", children: [jsx("span", { className: mentor
|
|
180886
|
+
? 'truncate text-gray-900 dark:text-gray-100'
|
|
180887
|
+
: 'text-gray-500 dark:text-gray-400', children: (mentor === null || mentor === void 0 ? void 0 : mentor.name) || t('profileApplicationsTab.selectAgentPlaceholder') }), jsx(ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" })] }), isMentorDropdownOpen && (jsxs("div", { className: "absolute z-50 mt-1 max-h-80 w-full rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-900", children: [jsx("div", { className: "border-b p-3 dark:border-gray-700", children: jsx(Input, { placeholder: t('profileApplicationsTab.searchAgentsPlaceholder'), value: mentorSearchTerm, onChange: (e) => setMentorSearchTerm(e.target.value), onClick: (e) => e.stopPropagation(), onFocus: (e) => e.stopPropagation(), className: "h-8",
|
|
180888
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
180889
|
+
autoFocus: true }) }), jsx("div", { ref: mentorScrollRef, role: "listbox", "aria-label": t('profileApplicationsTab.placementAgentLabel'), className: "max-h-60 overflow-y-auto", style: {
|
|
180890
|
+
scrollbarWidth: 'thin',
|
|
180891
|
+
scrollbarColor: '#d1d5db #f3f4f6',
|
|
180892
|
+
overscrollBehavior: 'contain',
|
|
180893
|
+
}, onScroll: handleMentorScroll, children: isLoadingMentors && mentorCurrentPage === 1 ? (jsx("div", { className: "p-3 text-sm text-gray-500", children: t('profileApplicationsTab.loadingAgents') })) : accumulatedMentors.length === 0 ? (jsx("div", { className: "p-3 text-sm text-gray-500", children: t('profileApplicationsTab.noAgentsFound') })) : (jsxs(Fragment$1, { children: [accumulatedMentors.map((option) => {
|
|
180894
|
+
const selected = (mentor === null || mentor === void 0 ? void 0 : mentor.unique_id) === option.unique_id;
|
|
180895
|
+
return (jsxs("button", { type: "button", role: "option", "aria-selected": selected,
|
|
180896
|
+
// Single-select: picking an agent closes the picker.
|
|
180897
|
+
onClick: () => {
|
|
180898
|
+
setMentor(option);
|
|
180899
|
+
setIsMentorDropdownOpen(false);
|
|
180900
|
+
}, className: "flex w-full items-center justify-between gap-2 p-3 text-left text-sm font-medium hover:bg-gray-50 dark:hover:bg-gray-800", children: [jsx("span", { className: "min-w-0 truncate", children: option.name || option.unique_id }), selected && (jsx(Check, { className: "h-4 w-4 shrink-0 text-blue-600 dark:text-blue-400" }))] }, option.unique_id || option.id));
|
|
180901
|
+
}), isFetchingMentors && mentorCurrentPage > 1 && (jsxs("div", { className: "flex items-center justify-center gap-2 p-3 text-sm text-gray-500", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" }), t('inviteCoursesTab.loadingMore')] }))] })) })] }))] })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "placement-label", children: t('profileApplicationsTab.placementSubjectLabel') }), jsx(Input, { id: "placement-label", value: label, onChange: (e) => setLabel(e.target.value) })] })] }), jsxs(DialogFooter, { children: [jsx(Button$1, { variant: "outline", onClick: onClose, children: t('profileApplicationsTab.cancel') }), jsx(Button$1, { className: "bg-gradient-to-r from-blue-600 to-blue-400 text-white hover:opacity-90", onClick: handleCreate, disabled: isLoading || !candidateId || !mentor, children: t('profileApplicationsTab.assignPlacement') })] })] }) }));
|
|
180902
|
+
}
|
|
180903
|
+
/** Course assignments for accepted students. */
|
|
180904
|
+
function AssignmentsSection({ platformKey, submissionId, detail, candidateLabel, onAdd, onChanged, }) {
|
|
180905
|
+
var _a;
|
|
180906
|
+
const t = useT();
|
|
180907
|
+
const [removeAssignment] = useDeleteApplicationCourseAssignmentMutation();
|
|
180908
|
+
const assignments = (_a = detail.course_assignments) !== null && _a !== void 0 ? _a : [];
|
|
180909
|
+
const handleRemove = async (assignmentId) => {
|
|
180910
|
+
try {
|
|
180911
|
+
await removeAssignment({
|
|
180912
|
+
submission_id: submissionId,
|
|
180913
|
+
assignment_id: assignmentId,
|
|
180914
|
+
platform_key: platformKey,
|
|
180915
|
+
}).unwrap();
|
|
180916
|
+
toast.success(t('profileApplicationsTab.assignmentRemoved'));
|
|
180917
|
+
onChanged();
|
|
180918
|
+
}
|
|
180919
|
+
catch (_a) {
|
|
180920
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
180921
|
+
}
|
|
180922
|
+
};
|
|
180923
|
+
return (jsxs("section", { className: "space-y-3", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs("h5", { className: "flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-gray-100", children: [jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-400", children: jsx(BookOpen, { className: "h-3.5 w-3.5" }) }), t('profileApplicationsTab.assignmentsHeading')] }), jsxs(Button$1, { variant: "outline", className: "h-8 px-3 text-xs", onClick: onAdd, children: [jsx(Plus, { className: "mr-1 h-3.5 w-3.5" }), t('profileApplicationsTab.assignCourse')] })] }), assignments.length === 0 ? (jsx("p", { className: "text-sm text-gray-400", children: t('profileApplicationsTab.noAssignments') })) : (jsx("div", { className: "grid gap-2", children: assignments.map((assignment) => (jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-xl border border-gray-200 bg-white px-4 py-2.5 text-sm shadow-sm dark:border-gray-700 dark:bg-gray-900", children: [jsxs("div", { className: "min-w-0", children: [jsx("span", { className: "font-medium text-gray-900 dark:text-gray-100", children: candidateLabel(assignment.candidate_id) }), jsxs("span", { className: "break-all text-gray-500", children: [" \u00B7 ", assignment.course_id] }), (assignment.started || assignment.expired) && (jsxs("p", { className: "mt-0.5 text-xs text-gray-400", children: [fmtDateTime(assignment.started), " \u2192 ", fmtDateTime(assignment.expired)] }))] }), jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400 hover:text-red-600", "aria-label": t('profileApplicationsTab.removeAssignment'), onClick: () => handleRemove(assignment.id), children: jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, assignment.id))) }))] }));
|
|
180924
|
+
}
|
|
180925
|
+
const COURSES_PER_PAGE = 20;
|
|
180926
|
+
function AssignmentDialog({ platformKey, submissionId, candidates, onClose, onDone, }) {
|
|
180927
|
+
var _a, _b;
|
|
180928
|
+
const t = useT();
|
|
180929
|
+
const [candidateId, setCandidateId] = useState(candidates[0] ? String(candidates[0].id) : '');
|
|
180930
|
+
const [started, setStarted] = useState('');
|
|
180931
|
+
const [expired, setExpired] = useState('');
|
|
180932
|
+
const [createAssignments, { isLoading }] = useCreateApplicationCourseAssignmentsMutation();
|
|
180933
|
+
// Course picker (mirrors the invite courses tab): debounced personalized
|
|
180934
|
+
// search, page accumulation with scroll-to-load-more, checkbox multi-select.
|
|
180935
|
+
const [selectedCourses, setSelectedCourses] = useState([]);
|
|
180936
|
+
const [courseSearchTerm, setCourseSearchTerm] = useState('');
|
|
180937
|
+
const [debouncedCourseSearch, setDebouncedCourseSearch] = useState('');
|
|
180938
|
+
const [isCourseDropdownOpen, setIsCourseDropdownOpen] = useState(false);
|
|
180939
|
+
const [courseCurrentPage, setCourseCurrentPage] = useState(1);
|
|
180940
|
+
const [accumulatedCourses, setAccumulatedCourses] = useState([]);
|
|
180941
|
+
const courseDropdownRef = useRef(null);
|
|
180942
|
+
const courseScrollRef = useRef(null);
|
|
180943
|
+
useEffect(() => {
|
|
180944
|
+
const handler = setTimeout(() => setDebouncedCourseSearch(courseSearchTerm), 300);
|
|
180945
|
+
return () => clearTimeout(handler);
|
|
180946
|
+
}, [courseSearchTerm]);
|
|
180947
|
+
const { data: coursesData, isLoading: isLoadingCourses, isFetching: isFetchingCourses, } = useGetPersonnalizedSearchQuery([
|
|
180948
|
+
{
|
|
180949
|
+
username: getUserName$1(),
|
|
180950
|
+
content: ['courses'],
|
|
180951
|
+
query: debouncedCourseSearch || '',
|
|
180952
|
+
returnFacet: false,
|
|
180953
|
+
tenant: platformKey,
|
|
180954
|
+
alphabetical: true,
|
|
180955
|
+
limit: COURSES_PER_PAGE,
|
|
180956
|
+
offset: (courseCurrentPage - 1) * COURSES_PER_PAGE,
|
|
180957
|
+
},
|
|
180958
|
+
], { skip: !isCourseDropdownOpen });
|
|
180959
|
+
// New search restarts paging.
|
|
180960
|
+
useEffect(() => {
|
|
180961
|
+
setCourseCurrentPage(1);
|
|
180962
|
+
setAccumulatedCourses([]);
|
|
180963
|
+
}, [debouncedCourseSearch]);
|
|
180964
|
+
// Accumulate courses across pages (deduplicated by id).
|
|
180965
|
+
useEffect(() => {
|
|
180966
|
+
const results = coursesData === null || coursesData === void 0 ? void 0 : coursesData.results;
|
|
180967
|
+
if (!results)
|
|
180968
|
+
return;
|
|
180969
|
+
const newCourses = results
|
|
180970
|
+
.filter((item) => item.type === 'course' && item.data)
|
|
180971
|
+
.map((item) => item.data);
|
|
180972
|
+
if (courseCurrentPage === 1) {
|
|
180973
|
+
setAccumulatedCourses(newCourses);
|
|
180974
|
+
}
|
|
180975
|
+
else {
|
|
180976
|
+
setAccumulatedCourses((prev) => {
|
|
180977
|
+
const existingIds = new Set(prev.map((c) => c.id));
|
|
180978
|
+
return [...prev, ...newCourses.filter((c) => !existingIds.has(c.id))];
|
|
180979
|
+
});
|
|
180980
|
+
}
|
|
180981
|
+
}, [coursesData, courseCurrentPage]);
|
|
180982
|
+
const hasMoreCourses = ((_b = (_a = coursesData === null || coursesData === void 0 ? void 0 : coursesData.results) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) === COURSES_PER_PAGE;
|
|
180983
|
+
const handleCourseScroll = () => {
|
|
180984
|
+
const scrollContainer = courseScrollRef.current;
|
|
180985
|
+
if (!scrollContainer || isFetchingCourses || !hasMoreCourses)
|
|
180986
|
+
return;
|
|
180987
|
+
const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
|
|
180988
|
+
if (scrollHeight - scrollTop - clientHeight < 50) {
|
|
180989
|
+
setCourseCurrentPage((prev) => prev + 1);
|
|
180990
|
+
}
|
|
180991
|
+
};
|
|
180992
|
+
// Close the picker on outside clicks.
|
|
180993
|
+
useEffect(() => {
|
|
180994
|
+
const handleClickOutside = (event) => {
|
|
180995
|
+
if (courseDropdownRef.current && !courseDropdownRef.current.contains(event.target)) {
|
|
180996
|
+
setIsCourseDropdownOpen(false);
|
|
180997
|
+
}
|
|
180998
|
+
};
|
|
180999
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
181000
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
181001
|
+
}, []);
|
|
181002
|
+
const toggleCourse = (course, checked) => {
|
|
181003
|
+
setSelectedCourses((prev) => checked ? [...prev, course] : prev.filter((c) => c.id !== course.id));
|
|
181004
|
+
};
|
|
181005
|
+
const handleCreate = async () => {
|
|
181006
|
+
var _a, _b;
|
|
181007
|
+
try {
|
|
181008
|
+
const result = (await createAssignments({
|
|
181009
|
+
submission_id: submissionId,
|
|
181010
|
+
platform_key: platformKey,
|
|
181011
|
+
assignments: selectedCourses.map((course) => ({
|
|
181012
|
+
candidate_id: Number(candidateId),
|
|
181013
|
+
course_id: course.course_id,
|
|
181014
|
+
...(started ? { started: new Date(started).toISOString() } : {}),
|
|
181015
|
+
...(expired ? { expired: new Date(expired).toISOString() } : {}),
|
|
181016
|
+
})),
|
|
181017
|
+
}).unwrap());
|
|
181018
|
+
const itemError = (_b = (_a = result.results) === null || _a === void 0 ? void 0 : _a.find((row) => row.error)) === null || _b === void 0 ? void 0 : _b.error;
|
|
181019
|
+
if (itemError) {
|
|
181020
|
+
toast.error(itemError === 'already_assigned'
|
|
181021
|
+
? t('profileApplicationsTab.alreadyAssigned')
|
|
181022
|
+
: itemError === 'course_platform_mismatch'
|
|
181023
|
+
? t('profileApplicationsTab.coursePlatformMismatch')
|
|
181024
|
+
: t('profileApplicationsTab.actionFailed'));
|
|
181025
|
+
return;
|
|
181026
|
+
}
|
|
181027
|
+
toast.success(t('profileApplicationsTab.courseAssigned'));
|
|
181028
|
+
onDone();
|
|
181029
|
+
}
|
|
181030
|
+
catch (_c) {
|
|
181031
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
181032
|
+
}
|
|
181033
|
+
};
|
|
181034
|
+
return (jsx(Dialog, { open: true, onOpenChange: (open) => !open && onClose(), children: jsxs(DialogContent, { className: "max-w-md", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: t('profileApplicationsTab.assignCourse') }) }), jsxs("div", { className: "space-y-3", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { children: t('profileApplicationsTab.studentLabel') }), jsxs(Select$1, { value: candidateId, onValueChange: setCandidateId, children: [jsx(SelectTrigger, { "aria-label": t('profileApplicationsTab.studentLabel'), children: jsx(SelectValue, {}) }), jsx(SelectContent, { children: candidates.map((candidate) => {
|
|
181035
|
+
var _a;
|
|
181036
|
+
return (jsx(SelectItem, { value: String(candidate.id), children: candidate.label || ((_a = candidate.details) === null || _a === void 0 ? void 0 : _a.name) || `#${candidate.id}` }, candidate.id));
|
|
181037
|
+
}) })] })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { children: t('profileApplicationsTab.coursesLabel') }), jsxs("div", { className: "relative", ref: courseDropdownRef, children: [jsxs(Button$1, { type: "button", variant: "outline", className: "w-full justify-between border-gray-300 bg-white hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:hover:bg-gray-800", onClick: () => setIsCourseDropdownOpen((open) => !open), "aria-expanded": isCourseDropdownOpen, "aria-haspopup": "listbox", children: [jsx("span", { className: "text-gray-500 dark:text-gray-400", children: selectedCourses.length > 0
|
|
181038
|
+
? t('inviteCoursesTab.coursesSelected', { count: selectedCourses.length })
|
|
181039
|
+
: t('inviteCoursesTab.selectCoursesPlaceholder') }), jsx(ChevronDown, { className: "h-4 w-4 opacity-50" })] }), isCourseDropdownOpen && (jsxs("div", { className: "absolute z-50 mt-1 max-h-80 w-full rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-900", children: [jsx("div", { className: "border-b p-3 dark:border-gray-700", children: jsx(Input, { placeholder: t('inviteCoursesTab.searchCoursesPlaceholder'), value: courseSearchTerm, onChange: (e) => setCourseSearchTerm(e.target.value), onClick: (e) => e.stopPropagation(), onFocus: (e) => e.stopPropagation(), className: "h-8",
|
|
181040
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
181041
|
+
autoFocus: true }) }), jsx("div", { ref: courseScrollRef, className: "max-h-60 overflow-y-auto", style: {
|
|
181042
|
+
scrollbarWidth: 'thin',
|
|
181043
|
+
scrollbarColor: '#d1d5db #f3f4f6',
|
|
181044
|
+
overscrollBehavior: 'contain',
|
|
181045
|
+
}, onScroll: handleCourseScroll, children: isLoadingCourses && courseCurrentPage === 1 ? (jsx("div", { className: "p-3 text-sm text-gray-500", children: t('inviteCoursesTab.loadingCourses') })) : accumulatedCourses.length === 0 ? (jsx("div", { className: "p-3 text-sm text-gray-500", children: t('inviteCoursesTab.noCoursesFound') })) : (jsxs(Fragment$1, { children: [accumulatedCourses.map((course) => (jsxs("div", { className: "flex cursor-pointer items-start space-x-2 p-3 hover:bg-gray-50 dark:hover:bg-gray-800", onClick: (e) => e.stopPropagation(), children: [jsx(Checkbox, { id: `assign-course-${course.id}`, checked: selectedCourses.some((c) => c.id === course.id), className: "mt-1", onCheckedChange: (checked) => toggleCourse(course, checked === true) }), jsx(Label$1, { htmlFor: `assign-course-${course.id}`, className: "flex-1 cursor-pointer text-sm font-medium", children: course.name || course.course_id })] }, course.id))), isFetchingCourses && courseCurrentPage > 1 && (jsxs("div", { className: "flex items-center justify-center gap-2 p-3 text-sm text-gray-500", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" }), t('inviteCoursesTab.loadingMore')] }))] })) })] }))] }), selectedCourses.length > 0 && (jsx("div", { className: "flex flex-wrap gap-2 pt-1", children: selectedCourses.map((course) => (jsxs("span", { className: "flex items-center gap-1 rounded-full bg-blue-100 px-2 py-1 text-xs text-blue-800 dark:bg-blue-900/40 dark:text-blue-300", children: [jsx("span", { className: "max-w-48 truncate", children: course.name || course.course_id }), jsx("button", { type: "button", onClick: () => toggleCourse(course, false), className: "ml-0.5 rounded-full p-0.5 hover:bg-blue-200 dark:hover:bg-blue-800", "aria-label": t('profileApplicationsTab.removeCourse', {
|
|
181046
|
+
course: course.name || course.course_id,
|
|
181047
|
+
}), children: jsx(X$2, { className: "h-3 w-3" }) })] }, course.id))) }))] }), jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "assignment-started", children: t('profileApplicationsTab.startDateLabel') }), jsx(Input, { id: "assignment-started", type: "date", value: started, onChange: (e) => setStarted(e.target.value) })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "assignment-expired", children: t('profileApplicationsTab.endDateLabel') }), jsx(Input, { id: "assignment-expired", type: "date", value: expired, onChange: (e) => setExpired(e.target.value) })] })] })] }), jsxs(DialogFooter, { children: [jsx(Button$1, { variant: "outline", onClick: onClose, children: t('profileApplicationsTab.cancel') }), jsx(Button$1, { className: "bg-gradient-to-r from-blue-600 to-blue-400 text-white hover:opacity-90", onClick: handleCreate, disabled: isLoading || !candidateId || selectedCourses.length === 0, children: t('profileApplicationsTab.assignCourse') })] })] }) }));
|
|
181048
|
+
}
|
|
181049
|
+
/** Internal notes: list, add, edit, delete. Never shown to applicants. */
|
|
181050
|
+
function NotesSection({ platformKey, submissionId, detail, }) {
|
|
181051
|
+
var _a;
|
|
181052
|
+
const t = useT();
|
|
181053
|
+
const [body, setBody] = useState('');
|
|
181054
|
+
const [kind, setKind] = useState('general');
|
|
181055
|
+
const [editingId, setEditingId] = useState(null);
|
|
181056
|
+
const [editBody, setEditBody] = useState('');
|
|
181057
|
+
const [createNote, { isLoading: creating }] = useCreateApplicationNoteMutation();
|
|
181058
|
+
const [updateNote] = useUpdateApplicationNoteMutation();
|
|
181059
|
+
const [deleteNote] = useDeleteApplicationNoteMutation();
|
|
181060
|
+
const notes = (_a = detail.notes) !== null && _a !== void 0 ? _a : [];
|
|
181061
|
+
const handleAdd = async () => {
|
|
181062
|
+
if (!body.trim())
|
|
181063
|
+
return;
|
|
181064
|
+
try {
|
|
181065
|
+
await createNote({
|
|
181066
|
+
submission_id: submissionId,
|
|
181067
|
+
platform_key: platformKey,
|
|
181068
|
+
body: body.trim(),
|
|
181069
|
+
kind,
|
|
181070
|
+
}).unwrap();
|
|
181071
|
+
setBody('');
|
|
181072
|
+
}
|
|
181073
|
+
catch (_a) {
|
|
181074
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
181075
|
+
}
|
|
181076
|
+
};
|
|
181077
|
+
const handleSaveEdit = async (noteId) => {
|
|
181078
|
+
try {
|
|
181079
|
+
await updateNote({
|
|
181080
|
+
submission_id: submissionId,
|
|
181081
|
+
note_id: noteId,
|
|
181082
|
+
platform_key: platformKey,
|
|
181083
|
+
body: editBody.trim(),
|
|
181084
|
+
}).unwrap();
|
|
181085
|
+
setEditingId(null);
|
|
181086
|
+
}
|
|
181087
|
+
catch (_a) {
|
|
181088
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
181089
|
+
}
|
|
181090
|
+
};
|
|
181091
|
+
const handleDelete = async (noteId) => {
|
|
181092
|
+
try {
|
|
181093
|
+
await deleteNote({
|
|
181094
|
+
submission_id: submissionId,
|
|
181095
|
+
note_id: noteId,
|
|
181096
|
+
platform_key: platformKey,
|
|
181097
|
+
}).unwrap();
|
|
181098
|
+
}
|
|
181099
|
+
catch (_a) {
|
|
181100
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
181101
|
+
}
|
|
181102
|
+
};
|
|
181103
|
+
return (jsxs("section", { className: "space-y-3", children: [jsxs("h5", { className: "flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-gray-100", children: [jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-400", children: jsx(StickyNote, { className: "h-3.5 w-3.5" }) }), t('profileApplicationsTab.notesHeading')] }), jsx("div", { className: "space-y-2", children: notes.map((note) => (jsx("div", { className: "rounded-xl border border-gray-200 bg-white px-4 py-2.5 shadow-sm dark:border-gray-700 dark:bg-gray-900", children: editingId === note.id ? (jsxs("div", { className: "space-y-2", children: [jsx(Textarea, { rows: 2, value: editBody, onChange: (e) => setEditBody(e.target.value), "aria-label": t('profileApplicationsTab.editNote') }), jsxs("div", { className: "flex justify-end gap-2", children: [jsx(Button$1, { variant: "outline", className: "h-7 px-2.5 text-xs", onClick: () => setEditingId(null), children: t('profileApplicationsTab.cancel') }), jsx(Button$1, { className: "h-7 px-2.5 text-xs", onClick: () => handleSaveEdit(note.id), children: t('profileApplicationsTab.saveNote') })] })] })) : (jsxs("div", { className: "flex items-start justify-between gap-3", children: [jsxs("div", { className: "min-w-0", children: [jsx("p", { className: "whitespace-pre-wrap text-sm text-gray-800 dark:text-gray-200", children: note.body }), jsxs("p", { className: "mt-1 text-xs text-gray-400", children: [note.kind === 'interview' && (jsx(Badge, { variant: "outline", className: "mr-1.5 text-[10px] text-violet-600", children: t('profileApplicationsTab.noteKindInterview') })), note.author || '—', " \u00B7 ", fmtDateTime(note.created)] })] }), jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400", "aria-label": t('profileApplicationsTab.editNote'), onClick: () => {
|
|
181104
|
+
setEditingId(note.id);
|
|
181105
|
+
setEditBody(note.body);
|
|
181106
|
+
}, children: jsx(Pencil, { className: "h-3.5 w-3.5" }) }), jsx(Button$1, { variant: "ghost", className: "h-7 w-7 p-0 text-gray-400 hover:text-red-600", "aria-label": t('profileApplicationsTab.deleteNote'), onClick: () => handleDelete(note.id), children: jsx(Trash2, { className: "h-3.5 w-3.5" }) })] })] })) }, note.id))) }), jsxs("div", { className: "flex items-start gap-2", children: [jsx(Textarea, { rows: 3, value: body, onChange: (e) => setBody(e.target.value), placeholder: t('profileApplicationsTab.addNotePlaceholder'), "aria-label": t('profileApplicationsTab.addNotePlaceholder'), className: "flex-1" }), jsxs("div", { className: "flex flex-col gap-2", children: [jsxs(Select$1, { value: kind, onValueChange: (value) => setKind(value), children: [jsx(SelectTrigger, { className: "h-9 w-32 text-xs", "aria-label": t('profileApplicationsTab.noteKindLabel'), children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "general", children: t('profileApplicationsTab.noteKindGeneral') }), jsx(SelectItem, { value: "interview", children: t('profileApplicationsTab.noteKindInterview') })] })] }), jsx(Button$1, { className: "h-9 px-3 text-xs", onClick: handleAdd, disabled: creating || !body.trim(), children: t('profileApplicationsTab.addNote') })] })] })] }));
|
|
181107
|
+
}
|
|
181108
|
+
|
|
181109
|
+
/**
|
|
181110
|
+
* The application ban list: families barred from reapplying (created via
|
|
181111
|
+
* decline-with-block). Unblocking lets them apply again; a cooldown may still
|
|
181112
|
+
* apply. Blocked users are never told they are blocked.
|
|
181113
|
+
*/
|
|
181114
|
+
function BlocksView({ platformKey, onBack, }) {
|
|
181115
|
+
const t = useT();
|
|
181116
|
+
const { data, isLoading, error } = useGetApplicationBlocksQuery({ platform_key: platformKey }, { refetchOnMountOrArgChange: true });
|
|
181117
|
+
const [deleteBlock, { isLoading: deleting }] = useDeleteApplicationBlockMutation();
|
|
181118
|
+
const blocks = Array.isArray(data === null || data === void 0 ? void 0 : data.results)
|
|
181119
|
+
? data.results
|
|
181120
|
+
: [];
|
|
181121
|
+
const handleUnblock = async (blockId) => {
|
|
181122
|
+
try {
|
|
181123
|
+
await deleteBlock({ block_id: blockId, platform_key: platformKey }).unwrap();
|
|
181124
|
+
toast.success(t('profileApplicationsTab.unblocked'));
|
|
181125
|
+
}
|
|
181126
|
+
catch (_a) {
|
|
181127
|
+
toast.error(t('profileApplicationsTab.actionFailed'));
|
|
181128
|
+
}
|
|
181129
|
+
};
|
|
181130
|
+
return (jsxs("div", { className: "space-y-5 pt-4", children: [jsxs("button", { type: "button", onClick: onBack, className: "inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 dark:text-gray-300", children: [jsx(ArrowLeft, { className: "h-4 w-4" }), t('profileApplicationsTab.backToForms')] }), jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-blue-100 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400", children: jsx(Ban, { className: "h-4 w-4" }) }), jsxs("div", { children: [jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: t('profileApplicationsTab.blockedUsers') }), jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.blockedUsersDescription') })] })] }), jsx("div", { className: "overflow-x-auto rounded-xl border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-900", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { children: jsxs("tr", { className: "border-b border-gray-200 bg-gray-50 text-left text-xs uppercase tracking-wide text-gray-500 dark:border-gray-700 dark:bg-gray-800/50", children: [jsx("th", { className: "px-4 py-2.5 font-medium", children: t('profileApplicationsTab.colUser') }), jsx("th", { className: "px-4 py-2.5 font-medium", children: t('profileApplicationsTab.colReason') }), jsx("th", { className: "px-4 py-2.5 font-medium", children: t('profileApplicationsTab.colBlockedBy') }), jsx("th", { className: "px-4 py-2.5 text-right font-medium", children: t('profileApplicationsTab.colActions') })] }) }), jsx("tbody", { className: "divide-y divide-gray-100 dark:divide-gray-800", children: isLoading ? ([0, 1].map((i) => (jsx("tr", { children: [0, 1, 2, 3].map((j) => (jsx("td", { className: "px-4 py-3", children: jsx(Skeleton, { className: "h-4 w-full max-w-32" }) }, j))) }, i)))) : error ? (jsx("tr", { children: jsx("td", { colSpan: 4, className: "px-4 py-10 text-center text-red-600", children: t('profileApplicationsTab.loadError') }) })) : blocks.length === 0 ? (jsx("tr", { children: jsx("td", { colSpan: 4, className: "px-4 py-10 text-center text-gray-400", children: t('profileApplicationsTab.noBlockedUsers') }) })) : (blocks.map((block) => {
|
|
181131
|
+
var _a, _b;
|
|
181132
|
+
return (jsxs("tr", { className: "hover:bg-blue-50/40 dark:hover:bg-gray-800/40", children: [jsx("td", { className: "whitespace-nowrap px-4 py-3 font-medium text-gray-900 dark:text-gray-100", children: ((_a = block.user) === null || _a === void 0 ? void 0 : _a.username) || ((_b = block.user) === null || _b === void 0 ? void 0 : _b.email) || '—' }), jsx("td", { className: "px-4 py-3 text-gray-500", children: block.reason || '—' }), jsx("td", { className: "whitespace-nowrap px-4 py-3 text-gray-500", children: block.created_by || '—' }), jsx("td", { className: "whitespace-nowrap px-4 py-3 text-right", children: jsx(Button$1, { variant: "outline", className: "h-8 px-3 text-xs", disabled: deleting, onClick: () => handleUnblock(block.id), children: t('profileApplicationsTab.unblock') }) })] }, block.id));
|
|
181133
|
+
})) })] }) })] }));
|
|
181134
|
+
}
|
|
181135
|
+
|
|
181136
|
+
const FILTER_ORDER = ['all', ...APPLICATION_STATUSES];
|
|
181137
|
+
const PAGE_SIZE$7 = 20;
|
|
181138
|
+
const SEARCH_DEBOUNCE_MS = 300;
|
|
181139
|
+
/**
|
|
181140
|
+
* The public application URL on the auth SPA for one form of a platform
|
|
181141
|
+
* (`<auth-url>/applications/<platform-key>/<form-id>?app=<spa>`), or null when
|
|
181142
|
+
* the auth SPA URL cannot be resolved. `authSpaUrl` (prop) wins; otherwise the
|
|
181143
|
+
* runtime-injected env (`window.__ENV__.NEXT_PUBLIC_AUTH_URL`) is used. With no
|
|
181144
|
+
* `formId` the link resolves the platform's default form (404 when none). The
|
|
181145
|
+
* `app` param carries the SPA the applicant is sent to after submitting;
|
|
181146
|
+
* absent/unknown means the mentor SPA.
|
|
181147
|
+
*/
|
|
181148
|
+
function buildApplicationLink(platformKey, formId, authSpaUrl, app) {
|
|
181149
|
+
var _a, _b;
|
|
181150
|
+
const base = (_b = authSpaUrl !== null && authSpaUrl !== void 0 ? authSpaUrl : (typeof window !== 'undefined'
|
|
181151
|
+
? (_a = window.__ENV__) === null || _a === void 0 ? void 0 : _a.NEXT_PUBLIC_AUTH_URL
|
|
181152
|
+
: undefined)) !== null && _b !== void 0 ? _b : process.env.NEXT_PUBLIC_AUTH_URL;
|
|
181153
|
+
if (!base)
|
|
181154
|
+
return null;
|
|
181155
|
+
const root = `${trimTrailingSlashes(base)}/applications/${encodeURIComponent(platformKey)}`;
|
|
181156
|
+
const path = formId != null ? `${root}/${encodeURIComponent(String(formId))}` : root;
|
|
181157
|
+
return `${path}?app=${encodeURIComponent(app || 'mentor')}`;
|
|
181158
|
+
}
|
|
181159
|
+
/**
|
|
181160
|
+
* Applications management. Entry view lists the platform's application forms
|
|
181161
|
+
* (a platform may run several; exactly one is the default). From there:
|
|
181162
|
+
* per-form (or all-form) submission pipelines, the submission workspace with
|
|
181163
|
+
* per-student decisions, the form/question editor, and the ban list.
|
|
181164
|
+
*/
|
|
181165
|
+
function ApplicationsTab({ platformKey, authSpaUrl, currentSpa, }) {
|
|
181166
|
+
const [view, setView] = useState({ name: 'forms' });
|
|
181167
|
+
if (view.name === 'editor') {
|
|
181168
|
+
return (jsx(QuestionsEditor, { platformKey: platformKey, initialFormId: view.formId, startNew: view.startNew, onBack: () => setView({ name: 'forms' }) }));
|
|
181169
|
+
}
|
|
181170
|
+
if (view.name === 'blocks') {
|
|
181171
|
+
return jsx(BlocksView, { platformKey: platformKey, onBack: () => setView({ name: 'forms' }) });
|
|
181172
|
+
}
|
|
181173
|
+
if (view.name === 'detail') {
|
|
181174
|
+
return (jsx(SubmissionDetail, { platformKey: platformKey, submissionId: view.submissionId, onBack: () => setView({ name: 'submissions', form: view.form }) }));
|
|
181175
|
+
}
|
|
181176
|
+
if (view.name === 'submissions') {
|
|
181177
|
+
return (jsx(SubmissionsPipeline, { platformKey: platformKey, form: view.form, onBack: () => setView({ name: 'forms' }), onOpen: (submissionId) => setView({ name: 'detail', submissionId, form: view.form }) }));
|
|
181178
|
+
}
|
|
181179
|
+
return (jsx(FormsList, { platformKey: platformKey, authSpaUrl: authSpaUrl, currentSpa: currentSpa, onViewSubmissions: (form) => setView({ name: 'submissions', form }), onEditForm: (formId) => setView({ name: 'editor', formId, startNew: false }), onNewForm: () => setView({ name: 'editor', formId: null, startNew: true }), onViewBlocks: () => setView({ name: 'blocks' }) }));
|
|
181180
|
+
}
|
|
181181
|
+
/** The entry view: every application form of the platform, with actions. */
|
|
181182
|
+
function FormsList({ platformKey, authSpaUrl, currentSpa, onViewSubmissions, onEditForm, onNewForm, onViewBlocks, }) {
|
|
181183
|
+
const t = useT();
|
|
181184
|
+
/** The form id whose link was just copied (transient "Copied" feedback). */
|
|
181185
|
+
const [copiedFormId, setCopiedFormId] = useState(null);
|
|
181186
|
+
const { data, isLoading, error } = useGetApplicationFormsQuery({ platform_key: platformKey }, { refetchOnMountOrArgChange: true });
|
|
181187
|
+
const { data: stats } = useGetApplicationsPipelineStatsQuery({ platform_key: platformKey }, { refetchOnMountOrArgChange: true });
|
|
181188
|
+
const forms = Array.isArray(data === null || data === void 0 ? void 0 : data.results)
|
|
181189
|
+
? data.results
|
|
181190
|
+
: Array.isArray(data)
|
|
181191
|
+
? data
|
|
181192
|
+
: [];
|
|
181193
|
+
const counts = statsCounts(stats);
|
|
181194
|
+
const total = Object.values(counts).reduce((sum, n) => sum + n, 0);
|
|
181195
|
+
const handleCopyLink = async (form) => {
|
|
181196
|
+
const link = buildApplicationLink(platformKey, form.id, authSpaUrl, currentSpa);
|
|
181197
|
+
if (!link)
|
|
181198
|
+
return;
|
|
181199
|
+
try {
|
|
181200
|
+
await navigator.clipboard.writeText(link);
|
|
181201
|
+
setCopiedFormId(form.id);
|
|
181202
|
+
setTimeout(() => setCopiedFormId((current) => (current === form.id ? null : current)), 2000);
|
|
181203
|
+
}
|
|
181204
|
+
catch (_a) {
|
|
181205
|
+
/* Clipboard unavailable (permissions/insecure context). */
|
|
181206
|
+
}
|
|
181207
|
+
};
|
|
181208
|
+
return (jsxs("div", { className: "space-y-5 pt-4", children: [jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [jsxs(Button$1, { variant: "outline", className: "h-[35px]", onClick: onViewBlocks, children: [jsx(Ban, { className: "mr-1.5 h-4 w-4" }), t('profileApplicationsTab.blockedUsers')] }), jsxs(Button$1, { variant: "outline", className: "h-[35px]", onClick: () => onViewSubmissions(null), children: [jsx(FileText, { className: "mr-1.5 h-4 w-4" }), t('profileApplicationsTab.allSubmissions', { count: total })] }), jsxs(Button$1, { onClick: onNewForm, className: "h-[35px] bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "mr-1.5 h-4 w-4" }), t('profileApplicationsTab.newForm')] })] }), isLoading ? (jsx("div", { className: "flex h-64 items-center justify-center", children: jsx(Spinner, {}) })) : (jsx("div", { className: "overflow-x-auto rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "border-b border-gray-200 bg-gray-50 dark:border-gray-600 dark:bg-gray-700", children: jsxs("tr", { children: [jsx("th", { className: "w-[55%] px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.colFormName') }), jsx("th", { className: "w-[12%] px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.colVersion') }), jsx("th", { className: "w-[15%] px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.colState') }), jsx("th", { className: "w-[18%] px-6 py-3 text-right font-medium text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.colActions') })] }) }), jsx("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: error ? (jsx("tr", { children: jsx("td", { colSpan: 4, className: "px-6 py-8 text-center text-red-600", children: t('profileApplicationsTab.loadError') }) })) : forms.length === 0 ? (jsx("tr", { children: jsx("td", { colSpan: 4, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.noForms') }) })) : (forms.map((form) => {
|
|
181209
|
+
var _a, _b;
|
|
181210
|
+
return (jsxs("tr", { className: "transition-colors hover:bg-gray-50 dark:hover:bg-gray-700/50", children: [jsxs("td", { className: "px-6 py-4 text-sm text-gray-900 dark:text-gray-100", children: [jsx("span", { className: "font-medium", children: ((_a = form.schema) === null || _a === void 0 ? void 0 : _a.title) ||
|
|
181211
|
+
form.name ||
|
|
181212
|
+
t('profileApplicationsTab.untitledForm') }), form.is_default && (jsx(Badge, { variant: "outline", className: "ml-2 text-[11px] font-medium text-blue-600", children: t('profileApplicationsTab.defaultBadge') }))] }), jsx("td", { className: "whitespace-nowrap px-6 py-4 text-sm text-gray-600 dark:text-gray-300", children: form.version != null ? `v${form.version}` : '—' }), jsx("td", { className: "px-6 py-4", children: jsx(Badge, { variant: "outline", className: form.enabled
|
|
181213
|
+
? 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-300 dark:border-blue-900'
|
|
181214
|
+
: 'bg-gray-100 text-gray-600 border-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600', children: t(form.enabled
|
|
181215
|
+
? 'profileApplicationsTab.formEnabled'
|
|
181216
|
+
: 'profileApplicationsTab.formDisabled') }) }), jsx("td", { className: "whitespace-nowrap px-6 py-4 text-right", children: jsxs("div", { className: "flex items-center justify-end gap-1", children: [buildApplicationLink(platformKey, form.id, authSpaUrl, currentSpa) && (jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs(Button$1, { variant: "ghost", className: copiedFormId === form.id
|
|
181217
|
+
? 'h-8 w-8 p-0 text-blue-600 dark:text-blue-400'
|
|
181218
|
+
: 'h-8 w-8 p-0 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100', onClick: () => handleCopyLink(form), "aria-label": t('profileApplicationsTab.copyFormLink', {
|
|
181219
|
+
form: ((_b = form.schema) === null || _b === void 0 ? void 0 : _b.title) ||
|
|
181220
|
+
form.name ||
|
|
181221
|
+
t('profileApplicationsTab.untitledForm'),
|
|
181222
|
+
}), children: [copiedFormId === form.id ? (jsx(Check, { className: "h-4 w-4" })) : (jsx(Copy, { className: "h-4 w-4" })), copiedFormId === form.id && (jsx("span", { role: "status", className: "sr-only", children: t('profileApplicationsTab.linkCopied') }))] }) }), jsx(TooltipContent, { children: t(copiedFormId === form.id
|
|
181223
|
+
? 'profileApplicationsTab.linkCopied'
|
|
181224
|
+
: 'profileApplicationsTab.copyLink') })] })), jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", className: "h-8 w-8 p-0 text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300", onClick: () => onViewSubmissions(form), "aria-label": t('profileApplicationsTab.viewSubmissions'), children: jsx(Eye, { className: "h-4 w-4" }) }) }), jsx(TooltipContent, { children: t('profileApplicationsTab.viewSubmissions') })] }), jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", className: "h-8 w-8 p-0 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100", onClick: () => onEditForm(form.id), "aria-label": t('profileApplicationsTab.editForm'), children: jsx(Pencil, { className: "h-4 w-4" }) }) }), jsx(TooltipContent, { children: t('profileApplicationsTab.editForm') })] })] }) })] }, form.id));
|
|
181225
|
+
})) })] }) }))] }));
|
|
181226
|
+
}
|
|
181227
|
+
/**
|
|
181228
|
+
* The submissions pipeline for one form (or every form). Filtering by form is
|
|
181229
|
+
* client-side per page: the pipeline endpoint has no form filter, so rows from
|
|
181230
|
+
* other forms are hidden after fetch.
|
|
181231
|
+
*/
|
|
181232
|
+
function SubmissionsPipeline({ platformKey, form, onBack, onOpen, }) {
|
|
181233
|
+
var _a;
|
|
181234
|
+
const t = useT();
|
|
181235
|
+
const [status, setStatus] = useState('all');
|
|
181236
|
+
const [search, setSearch] = useState('');
|
|
181237
|
+
const [debouncedSearch, setDebouncedSearch] = useState('');
|
|
181238
|
+
/** Structured student filter, "key=value" mapped to candidate.<key>=<value>. */
|
|
181239
|
+
const [candidateFilter, setCandidateFilter] = useState('');
|
|
181240
|
+
const [debouncedCandidateFilter, setDebouncedCandidateFilter] = useState('');
|
|
181241
|
+
const [sort, setSort] = useState('-created');
|
|
181242
|
+
const [page, setPage] = useState(1);
|
|
181243
|
+
useEffect(() => {
|
|
181244
|
+
const timer = setTimeout(() => {
|
|
181245
|
+
setDebouncedSearch(search.trim());
|
|
181246
|
+
setDebouncedCandidateFilter(candidateFilter.trim());
|
|
181247
|
+
}, SEARCH_DEBOUNCE_MS);
|
|
181248
|
+
return () => clearTimeout(timer);
|
|
181249
|
+
}, [search, candidateFilter]);
|
|
181250
|
+
// New filters restart paging.
|
|
181251
|
+
useEffect(() => {
|
|
181252
|
+
setPage(1);
|
|
181253
|
+
}, [status, debouncedSearch, debouncedCandidateFilter, sort]);
|
|
181254
|
+
const { data: stats } = useGetApplicationsPipelineStatsQuery({ platform_key: platformKey }, { refetchOnMountOrArgChange: true });
|
|
181255
|
+
const candidateParams = {};
|
|
181256
|
+
const eq = debouncedCandidateFilter.indexOf('=');
|
|
181257
|
+
if (eq > 0) {
|
|
181258
|
+
const key = debouncedCandidateFilter.slice(0, eq).trim();
|
|
181259
|
+
const value = debouncedCandidateFilter.slice(eq + 1).trim();
|
|
181260
|
+
if (key && value)
|
|
181261
|
+
candidateParams[`candidate.${key}`] = value;
|
|
181262
|
+
}
|
|
181263
|
+
const { data: pipeline, isLoading, isFetching, error, } = useGetApplicationsPipelineQuery({
|
|
181264
|
+
platform_key: platformKey,
|
|
181265
|
+
...(status !== 'all' ? { status } : {}),
|
|
181266
|
+
...(debouncedSearch ? { search: debouncedSearch } : {}),
|
|
181267
|
+
...(sort !== '-created' ? { sort } : {}),
|
|
181268
|
+
...candidateParams,
|
|
181269
|
+
page,
|
|
181270
|
+
page_size: PAGE_SIZE$7,
|
|
181271
|
+
}, {
|
|
181272
|
+
refetchOnMountOrArgChange: true,
|
|
181273
|
+
});
|
|
181274
|
+
const allRows = Array.isArray(pipeline === null || pipeline === void 0 ? void 0 : pipeline.results)
|
|
181275
|
+
? pipeline.results
|
|
181276
|
+
: [];
|
|
181277
|
+
const rows = form ? allRows.filter((row) => row.form_id === form.id) : allRows;
|
|
181278
|
+
const count = typeof (pipeline === null || pipeline === void 0 ? void 0 : pipeline.count) === 'number' ? pipeline.count : allRows.length;
|
|
181279
|
+
const totalPages = Math.max(1, Math.ceil(count / PAGE_SIZE$7));
|
|
181280
|
+
const counts = statsCounts(stats);
|
|
181281
|
+
const countFor = (key) => {
|
|
181282
|
+
if (!stats)
|
|
181283
|
+
return undefined;
|
|
181284
|
+
if (key === 'all') {
|
|
181285
|
+
return APPLICATION_STATUSES.reduce((sum, statusKey) => sum + (Number(counts[statusKey]) || 0), 0);
|
|
181286
|
+
}
|
|
181287
|
+
return Number(counts[key]) || 0;
|
|
181288
|
+
};
|
|
181289
|
+
const formatDate = (row) => {
|
|
181290
|
+
var _a;
|
|
181291
|
+
const raw = (_a = row.submitted_at) !== null && _a !== void 0 ? _a : row.created;
|
|
181292
|
+
if (!raw)
|
|
181293
|
+
return '—';
|
|
181294
|
+
const date = new Date(raw);
|
|
181295
|
+
return Number.isNaN(date.getTime()) ? '—' : date.toLocaleDateString();
|
|
181296
|
+
};
|
|
181297
|
+
return (jsxs("div", { className: "space-y-5 pt-4", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [jsxs("button", { type: "button", onClick: onBack, className: "inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 dark:text-gray-300", children: [jsx(ArrowLeft, { className: "h-4 w-4" }), t('profileApplicationsTab.backToForms')] }), jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: form
|
|
181298
|
+
? t('profileApplicationsTab.submissionsFor', {
|
|
181299
|
+
form: ((_a = form.schema) === null || _a === void 0 ? void 0 : _a.title) || form.name || t('profileApplicationsTab.untitledForm'),
|
|
181300
|
+
})
|
|
181301
|
+
: t('profileApplicationsTab.allSubmissionsHeading') })] }), jsx("div", { className: "flex flex-wrap gap-1.5", children: FILTER_ORDER.map((key) => {
|
|
181302
|
+
const isActive = status === key;
|
|
181303
|
+
const pillCount = countFor(key);
|
|
181304
|
+
return (jsxs("button", { type: "button", onClick: () => setStatus(key), className: `inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm transition-colors ${isActive
|
|
181305
|
+
? 'bg-blue-50 font-medium text-blue-700 dark:bg-blue-900/30 dark:text-blue-300'
|
|
181306
|
+
: 'text-gray-600 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-800/60'}`, children: [t(key === 'all' ? 'profileApplicationsTab.statusAll' : STATUS_LABEL_KEYS[key]), pillCount !== undefined && (jsx("span", { className: `text-xs ${isActive ? 'text-blue-600 dark:text-blue-300' : 'text-gray-400'}`, children: pillCount }))] }, key));
|
|
181307
|
+
}) }), jsxs("div", { className: "flex flex-wrap gap-2", children: [jsxs("div", { className: "relative min-w-56 max-w-sm flex-1", children: [jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" }), jsx(Input, { type: "text", value: search, onChange: (e) => setSearch(e.target.value), placeholder: t('profileApplicationsTab.searchPlaceholder'), className: "h-[35px] pl-10 pr-10 focus:border-blue-500 focus:ring-blue-500" }), search.length > 0 && (search.trim() !== debouncedSearch || isFetching) && (jsx(LoaderCircle, { "data-testid": "search-spinner", className: "absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-gray-400", "aria-hidden": "true" }))] }), jsx(Input, { value: candidateFilter, onChange: (e) => setCandidateFilter(e.target.value), placeholder: t('profileApplicationsTab.candidateFilterPlaceholder'), "aria-label": t('profileApplicationsTab.candidateFilterPlaceholder'), className: "h-[35px] w-48" }), jsxs(Select$1, { value: sort, onValueChange: setSort, children: [jsx(SelectTrigger, { className: "h-[35px] w-44", "aria-label": t('profileApplicationsTab.sortLabel'), children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "-created", children: t('profileApplicationsTab.sortNewest') }), jsx(SelectItem, { value: "created", children: t('profileApplicationsTab.sortOldest') }), jsx(SelectItem, { value: "-submitted_at", children: t('profileApplicationsTab.sortRecentlySubmitted') }), jsx(SelectItem, { value: "-modified", children: t('profileApplicationsTab.sortRecentlyModified') }), jsx(SelectItem, { value: "status", children: t('profileApplicationsTab.sortStatus') })] })] })] }), isLoading ? (jsx("div", { className: "flex h-64 items-center justify-center", children: jsx(Spinner, {}) })) : (jsxs(Fragment$1, { children: [jsx("div", { className: "overflow-x-auto rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "border-b border-gray-200 bg-gray-50 dark:border-gray-600 dark:bg-gray-700", children: jsxs("tr", { children: [jsx("th", { className: "w-[26%] px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.colApplicant') }), jsx("th", { className: "w-[40%] px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.colStudents') }), jsx("th", { className: "w-[17%] px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.colSubmitted') }), jsx("th", { className: "w-[17%] px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.colStatus') })] }) }), jsx("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: error ? (jsx("tr", { children: jsx("td", { colSpan: 4, className: "px-6 py-8 text-center text-red-600", children: t('profileApplicationsTab.loadError') }) })) : rows.length === 0 ? (jsx("tr", { children: jsx("td", { colSpan: 4, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('profileApplicationsTab.noMatch') }) })) : (rows.map((row) => {
|
|
181308
|
+
var _a, _b;
|
|
181309
|
+
return (jsxs("tr", { className: "cursor-pointer transition-colors hover:bg-gray-50 dark:hover:bg-gray-700/50", onClick: () => onOpen(row.id), children: [jsx("td", { className: "max-w-0 overflow-hidden text-ellipsis whitespace-nowrap px-6 py-4 text-sm text-gray-900 dark:text-gray-100", children: jsx("button", { type: "button", className: "block max-w-full truncate text-left font-medium hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none", children: applicantName((_a = row.user) !== null && _a !== void 0 ? _a : row.applicant) }) }), jsx("td", { className: "max-w-0 px-6 py-4", children: jsx("div", { className: "flex flex-wrap items-center gap-1", children: ((_b = row.candidates) !== null && _b !== void 0 ? _b : []).map((candidate, i) => {
|
|
181310
|
+
var _a;
|
|
181311
|
+
return (jsxs("span", { className: "inline-flex max-w-full items-center gap-1 overflow-hidden rounded-md bg-gray-100 px-2 py-0.5 text-xs text-gray-600 dark:bg-gray-700 dark:text-gray-300", children: [jsx("span", { className: "min-w-0 truncate", children: candidate.name || '—' }), candidate.status && (jsxs("span", { className: "shrink-0 whitespace-nowrap text-gray-400", children: ["\u00B7", ' ', STATUS_LABEL_KEYS[candidate.status]
|
|
181312
|
+
? t(STATUS_LABEL_KEYS[candidate.status])
|
|
181313
|
+
: candidate.status] }))] }, (_a = candidate.id) !== null && _a !== void 0 ? _a : i));
|
|
181314
|
+
}) }) }), jsx("td", { className: "whitespace-nowrap px-6 py-4 text-sm text-gray-600 dark:text-gray-300", children: formatDate(row) }), jsx("td", { className: "px-6 py-4", children: jsx(Badge, { variant: "outline", className: approvalTone(row.status), children: STATUS_LABEL_KEYS[row.status]
|
|
181315
|
+
? t(STATUS_LABEL_KEYS[row.status])
|
|
181316
|
+
: row.status }) })] }, row.id));
|
|
181317
|
+
})) })] }) }), jsx(AdvancedPagination, { variant: "footer", className: "-mx-6 -mb-6 mt-4 rounded-b-lg", totalPages: totalPages, currentPage: page, onPageChange: (newPage) => setPage(newPage), totalCount: count, itemsPerPage: PAGE_SIZE$7, itemLabel: "applications" })] }))] }));
|
|
181318
|
+
}
|
|
181319
|
+
|
|
178648
181320
|
const TabDescription = ({ icon, title, children }) => {
|
|
178649
181321
|
const t = useT();
|
|
178650
181322
|
const [expanded, setExpanded] = useState(false);
|
|
178651
181323
|
return (jsxs("div", { className: "flex gap-3 items-start border rounded-lg p-4 bg-blue-50/60 border-blue-100", children: [jsx("div", { className: "shrink-0 w-9 h-9 rounded-md flex items-center justify-center bg-blue-100 text-blue-600", children: icon }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("h3", { className: "text-sm font-semibold mb-1 text-blue-900 hidden sm:block", children: title }), jsx("p", { className: `text-sm text-gray-600 leading-relaxed sm:line-clamp-none! ${expanded ? '' : 'line-clamp-2'}`, children: children }), jsx("button", { type: "button", onClick: () => setExpanded((prev) => !prev), className: "mt-1 text-xs font-medium text-blue-700 hover:text-blue-800 sm:hidden", children: expanded ? t('profileAdmin.readLess') : t('profileAdmin.readMore') })] })] }));
|
|
178652
181324
|
};
|
|
178653
|
-
const Admin = ({ tenant, onInviteClick, hasUserTabPermission = false, hasGroupsTabPermission = false, hasRolesTabPermission = false, hasPoliciesTabPermission = false, hasTeamsTabPermission = false, hasAlertsTabPermission = false, hasInviteUserPermission = false, hasCreateTeamPermission = false, onLoadGroupPermissions, rbacPermissions = {}, enableRbac = false, showGradebookTab = false, currentSpa, }) => {
|
|
181325
|
+
const Admin = ({ tenant, onInviteClick, hasUserTabPermission = false, hasGroupsTabPermission = false, hasRolesTabPermission = false, hasPoliciesTabPermission = false, hasTeamsTabPermission = false, hasAlertsTabPermission = false, hasInviteUserPermission = false, hasCreateTeamPermission = false, onLoadGroupPermissions, rbacPermissions = {}, enableRbac = false, showGradebookTab = false, currentSpa, authSpaUrl, }) => {
|
|
178654
181326
|
const t = useT();
|
|
178655
|
-
return (jsx("div", { className: "border border-gray-200 rounded-lg p-6", children: jsxs(Tabs, { defaultValue: "users", "aria-label": t('profileAdmin.rbacManagementTabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('profileAdmin.managementTabsAriaLabel'), className: "max-w-full overflow-x-auto justify-start px-1", children: [hasUserTabPermission && (jsx(TabsTrigger, { value: "users", children: t('profileAdmin.usersTab') })), hasGroupsTabPermission && (jsx(TabsTrigger, { value: "groups", children: t('profileAdmin.groupsTab') })), hasRolesTabPermission && (jsx(TabsTrigger, { value: "roles", children: t('profileAdmin.rolesTab') })), hasPoliciesTabPermission && (jsx(TabsTrigger, { value: "policies", children: t('profileAdmin.policiesTab') })), hasTeamsTabPermission && (jsx(TabsTrigger, { value: "teams", children: t('profileAdmin.teamsTab') })), hasAlertsTabPermission && (jsx(TabsTrigger, { value: "alerts", children: t('profileAdmin.alertsTab') }))] }), hasUserTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "users", children: [jsx(TabDescription, { icon: jsx(Users, { className: "w-5 h-5" }), title: t('profileAdmin.usersTab'), children: t('profileAdmin.usersDescription') }), jsx(UsersTab, { tenant: tenant, onInviteClick: onInviteClick, currentSpa: currentSpa, showGradebookTab: showGradebookTab })] })), hasGroupsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "groups", children: [jsx(TabDescription, { icon: jsx(UsersRound, { className: "w-5 h-5" }), title: t('profileAdmin.groupsTab'), children: t('profileAdmin.groupsDescription') }), jsx(GroupsTab, { tenant: tenant })] })), hasRolesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "roles", children: [jsx(TabDescription, { icon: jsx(UserCog, { className: "w-5 h-5" }), title: t('profileAdmin.rolesTab'), children: t('profileAdmin.rolesDescription') }), jsx(RolesTab, { tenant: tenant })] })), hasPoliciesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "policies", children: [jsx(TabDescription, { icon: jsx(ScrollText, { className: "w-5 h-5" }), title: t('profileAdmin.policiesTab'), children: t('profileAdmin.policiesDescription') }), jsx(PoliciesTab, { tenant: tenant })] })), hasTeamsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "teams", children: [jsx(TabDescription, { icon: jsx(Shield, { className: "w-5 h-5" }), title: t('profileAdmin.teamsTab'), children: t('profileAdmin.teamsDescription') }), jsx(GroupsTab, { tenant: tenant, isTeam: true, onInviteClick: onInviteClick, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, rbacPermissions: rbacPermissions, enableRbac: enableRbac })] })), hasAlertsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "alerts", children: [jsx(TabDescription, { icon: jsx(BellRing, { className: "w-5 h-5" }), title: t('profileAdmin.alertsTab'), children: t('profileAdmin.alertsDescription') }), jsx(AlertsTab, { tenant: tenant })] }))] }) }));
|
|
181327
|
+
return (jsx("div", { className: "border border-gray-200 rounded-lg p-6", children: jsxs(Tabs, { defaultValue: "users", "aria-label": t('profileAdmin.rbacManagementTabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('profileAdmin.managementTabsAriaLabel'), className: "max-w-full overflow-x-auto justify-start px-1", children: [hasUserTabPermission && (jsx(TabsTrigger, { value: "users", children: t('profileAdmin.usersTab') })), hasGroupsTabPermission && (jsx(TabsTrigger, { value: "groups", children: t('profileAdmin.groupsTab') })), hasRolesTabPermission && (jsx(TabsTrigger, { value: "roles", children: t('profileAdmin.rolesTab') })), hasPoliciesTabPermission && (jsx(TabsTrigger, { value: "policies", children: t('profileAdmin.policiesTab') })), hasTeamsTabPermission && (jsx(TabsTrigger, { value: "teams", children: t('profileAdmin.teamsTab') })), hasAlertsTabPermission && (jsx(TabsTrigger, { value: "alerts", children: t('profileAdmin.alertsTab') })), jsx(TabsTrigger, { value: "applications", children: t('profileAdmin.applicationsTab') })] }), hasUserTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "users", children: [jsx(TabDescription, { icon: jsx(Users, { className: "w-5 h-5" }), title: t('profileAdmin.usersTab'), children: t('profileAdmin.usersDescription') }), jsx(UsersTab, { tenant: tenant, onInviteClick: onInviteClick, currentSpa: currentSpa, showGradebookTab: showGradebookTab })] })), hasGroupsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "groups", children: [jsx(TabDescription, { icon: jsx(UsersRound, { className: "w-5 h-5" }), title: t('profileAdmin.groupsTab'), children: t('profileAdmin.groupsDescription') }), jsx(GroupsTab, { tenant: tenant })] })), hasRolesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "roles", children: [jsx(TabDescription, { icon: jsx(UserCog, { className: "w-5 h-5" }), title: t('profileAdmin.rolesTab'), children: t('profileAdmin.rolesDescription') }), jsx(RolesTab, { tenant: tenant })] })), hasPoliciesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "policies", children: [jsx(TabDescription, { icon: jsx(ScrollText, { className: "w-5 h-5" }), title: t('profileAdmin.policiesTab'), children: t('profileAdmin.policiesDescription') }), jsx(PoliciesTab, { tenant: tenant })] })), hasTeamsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "teams", children: [jsx(TabDescription, { icon: jsx(Shield, { className: "w-5 h-5" }), title: t('profileAdmin.teamsTab'), children: t('profileAdmin.teamsDescription') }), jsx(GroupsTab, { tenant: tenant, isTeam: true, onInviteClick: onInviteClick, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, rbacPermissions: rbacPermissions, enableRbac: enableRbac })] })), hasAlertsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "alerts", children: [jsx(TabDescription, { icon: jsx(BellRing, { className: "w-5 h-5" }), title: t('profileAdmin.alertsTab'), children: t('profileAdmin.alertsDescription') }), jsx(AlertsTab, { tenant: tenant })] })), jsxs(TabsContent, { className: "mt-4", value: "applications", children: [jsx(TabDescription, { icon: jsx(ClipboardList, { className: "w-5 h-5" }), title: t('profileAdmin.applicationsTab'), children: t('profileAdmin.applicationsTabDescription') }), jsx(ApplicationsTab, { platformKey: tenant, authSpaUrl: authSpaUrl, currentSpa: currentSpa })] })] }) }));
|
|
178656
181328
|
};
|
|
178657
181329
|
|
|
178658
181330
|
function DeleteApiModal$1({ isOpen, onClose, apiKey, tenantKey }) {
|
|
@@ -180318,7 +182990,7 @@ function PreviousMonthButton(props) {
|
|
|
180318
182990
|
* @group Components
|
|
180319
182991
|
* @see https://daypicker.dev/guides/custom-components
|
|
180320
182992
|
*/
|
|
180321
|
-
function Root$
|
|
182993
|
+
function Root$5(props) {
|
|
180322
182994
|
const { rootRef, ...rest } = props;
|
|
180323
182995
|
return React__default.createElement("div", { ...rest, ref: rootRef });
|
|
180324
182996
|
}
|
|
@@ -180426,7 +183098,7 @@ var components$1 = /*#__PURE__*/Object.freeze({
|
|
|
180426
183098
|
NextMonthButton: NextMonthButton,
|
|
180427
183099
|
Option: Option,
|
|
180428
183100
|
PreviousMonthButton: PreviousMonthButton,
|
|
180429
|
-
Root: Root$
|
|
183101
|
+
Root: Root$5,
|
|
180430
183102
|
Select: Select,
|
|
180431
183103
|
Week: Week,
|
|
180432
183104
|
WeekNumber: WeekNumber,
|
|
@@ -212738,7 +215410,7 @@ var Toggle$1 = React.forwardRef((props, forwardedRef) => {
|
|
|
212738
215410
|
);
|
|
212739
215411
|
});
|
|
212740
215412
|
Toggle$1.displayName = NAME$3;
|
|
212741
|
-
var Root$
|
|
215413
|
+
var Root$4 = Toggle$1;
|
|
212742
215414
|
|
|
212743
215415
|
const toggleVariants = cva('inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground', {
|
|
212744
215416
|
variants: {
|
|
@@ -212757,8 +215429,8 @@ const toggleVariants = cva('inline-flex items-center justify-center rounded-md t
|
|
|
212757
215429
|
size: 'default',
|
|
212758
215430
|
},
|
|
212759
215431
|
});
|
|
212760
|
-
const Toggle = React.forwardRef(({ className, variant, size, ...props }, ref) => (jsx(Root$
|
|
212761
|
-
Toggle.displayName = Root$
|
|
215432
|
+
const Toggle = React.forwardRef(({ className, variant, size, ...props }, ref) => (jsx(Root$4, { ref: ref, className: cn(toggleVariants({ variant, size, className })), ...props })));
|
|
215433
|
+
Toggle.displayName = Root$4.displayName;
|
|
212762
215434
|
|
|
212763
215435
|
// src/separator.tsx
|
|
212764
215436
|
var NAME$2 = "Separator";
|
|
@@ -212783,10 +215455,10 @@ Separator$1.displayName = NAME$2;
|
|
|
212783
215455
|
function isValidOrientation(orientation) {
|
|
212784
215456
|
return ORIENTATIONS.includes(orientation);
|
|
212785
215457
|
}
|
|
212786
|
-
var Root$
|
|
215458
|
+
var Root$3 = Separator$1;
|
|
212787
215459
|
|
|
212788
|
-
const Separator = React.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (jsx(Root$
|
|
212789
|
-
Separator.displayName = Root$
|
|
215460
|
+
const Separator = React.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (jsx(Root$3, { ref: ref, decorative: decorative, orientation: orientation, className: cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", className), ...props })));
|
|
215461
|
+
Separator.displayName = Root$3.displayName;
|
|
212790
215462
|
|
|
212791
215463
|
// Custom Div extension to preserve div elements with inline styles
|
|
212792
215464
|
const Div = Node3.create({
|
|
@@ -213623,7 +216295,7 @@ function focusFirst(candidates, preventScroll = false) {
|
|
|
213623
216295
|
function wrapArray(array, startIndex) {
|
|
213624
216296
|
return array.map((_, index) => array[(startIndex + index) % array.length]);
|
|
213625
216297
|
}
|
|
213626
|
-
var Root$
|
|
216298
|
+
var Root$2 = RovingFocusGroup;
|
|
213627
216299
|
var Item = RovingFocusGroupItem;
|
|
213628
216300
|
|
|
213629
216301
|
// packages/react/use-size/src/useSize.tsx
|
|
@@ -213826,7 +216498,7 @@ var Radio = React.forwardRef(
|
|
|
213826
216498
|
type: "button",
|
|
213827
216499
|
role: "radio",
|
|
213828
216500
|
"aria-checked": checked,
|
|
213829
|
-
"data-state": getState
|
|
216501
|
+
"data-state": getState(checked),
|
|
213830
216502
|
"data-disabled": disabled ? "" : void 0,
|
|
213831
216503
|
disabled,
|
|
213832
216504
|
value,
|
|
@@ -213867,7 +216539,7 @@ var RadioIndicator = React.forwardRef(
|
|
|
213867
216539
|
return /* @__PURE__ */ jsx(Presence, { present: forceMount || context.checked, children: /* @__PURE__ */ jsx(
|
|
213868
216540
|
Primitive$1.span,
|
|
213869
216541
|
{
|
|
213870
|
-
"data-state": getState
|
|
216542
|
+
"data-state": getState(context.checked),
|
|
213871
216543
|
"data-disabled": context.disabled ? "" : void 0,
|
|
213872
216544
|
...indicatorProps,
|
|
213873
216545
|
ref: forwardedRef
|
|
@@ -213912,7 +216584,7 @@ var BubbleInput = (props) => {
|
|
|
213912
216584
|
}
|
|
213913
216585
|
);
|
|
213914
216586
|
};
|
|
213915
|
-
function getState
|
|
216587
|
+
function getState(checked) {
|
|
213916
216588
|
return checked ? "checked" : "unchecked";
|
|
213917
216589
|
}
|
|
213918
216590
|
var ARROW_KEYS = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"];
|
|
@@ -213956,7 +216628,7 @@ var RadioGroup$1 = React.forwardRef(
|
|
|
213956
216628
|
value,
|
|
213957
216629
|
onValueChange: setValue,
|
|
213958
216630
|
children: /* @__PURE__ */ jsx(
|
|
213959
|
-
Root$
|
|
216631
|
+
Root$2,
|
|
213960
216632
|
{
|
|
213961
216633
|
asChild: true,
|
|
213962
216634
|
...rovingFocusGroupScope,
|
|
@@ -254217,7 +256889,7 @@ function Account({ tenant, tenants = [], username, onInviteClick, email, mainPla
|
|
|
254217
256889
|
: 'text-gray-600 hover:bg-gray-50'}`, children: [jsx(CreditCard, { className: "h-4 w-4" }), jsx("span", { className: "hidden sm:inline", children: t('profileAccount.tabBilling') }), jsx("span", { className: "sm:hidden", children: t('profileAccount.tabBilling') })] }, 'billing'))] }) }) }), jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", style: { height: '100%' }, children: [jsxs("div", { className: "hidden lg:block flex-shrink-0 p-6 border-b border-gray-200 bg-white dark:bg-gray-900", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_b = (_a = TABS.find((t) => t.id === activeTab)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : activeTab }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'integrations' && t('profileAccount.descIntegrations'), activeTab === 'billing' && t('profileAccount.descBilling'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), jsxs("div", { className: "flex-1 p-6 space-y-6", style: {
|
|
254218
256890
|
overflowY: 'auto',
|
|
254219
256891
|
overflowX: 'hidden',
|
|
254220
|
-
}, children: [jsxs("div", { className: "lg:hidden mb-6", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_d = (_c = TABS.find((t) => t.id === activeTab)) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : activeTab }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'basic' && t('profileAccount.descBasic'), activeTab === 'social' && t('profileAccount.descSocial'), activeTab === 'security' && t('profileAccount.descSecurity'), activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), activeTab === 'management' && hasManagementPermissions && (jsx(Admin, { onInviteClick: onInviteClick, tenant: tenant, currentSpa: currentSPA, hasUserTabPermission: hasUserTabPermission, hasGroupsTabPermission: hasGroupsTabPermission, hasPoliciesTabPermission: hasPoliciesTabPermission, hasRolesTabPermission: hasRolesTabPermission, hasTeamsTabPermission: hasTeamsTabPermission, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, hasAlertsTabPermission: hasAlertsTabPermission, rbacPermissions: rbacPermissions, enableRbac: enableRbac, showGradebookTab: showGradebookTab })), activeTab === 'organization' && (jsx(OrganizationTab, { platformKey: tenant, tenant: tenants.find((ten) => ten.key === tenant), onTenantUpdate: onTenantUpdate, setOrganizationLogoFromOutside: setOrganizationLogo, defaultSupportPhone: defaultSupportPhone, enableSupportPhone: enableSupportPhone })), activeTab === 'integrations' && (jsx(IntegrationsTab, { tenantKey: tenant, username: username })), activeTab === 'billing' && (jsx(BillingTab, { tenant: tenant, userActiveApp: userActiveApp, username: username, currentUserEmail: email, mainPlatformKey: mainPlatformKey })), activeTab === 'monetization' && (jsx(MonetizationTab, { platformKey: tenant, authURL: authURL })), activeTab === 'datasets' && showBenchmarks && (jsx(BenchmarksTab, { tenantKey: tenant, username: username })), activeTab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenant, currentSPA: currentSPA, username: username, authURL: authURL, currentPlatformBaseDomain: currentPlatformBaseDomain }))] })] }), jsx(ToastProvider, {})] }));
|
|
256892
|
+
}, children: [jsxs("div", { className: "lg:hidden mb-6", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_d = (_c = TABS.find((t) => t.id === activeTab)) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : activeTab }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'basic' && t('profileAccount.descBasic'), activeTab === 'social' && t('profileAccount.descSocial'), activeTab === 'security' && t('profileAccount.descSecurity'), activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), activeTab === 'management' && hasManagementPermissions && (jsx(Admin, { authSpaUrl: authURL, onInviteClick: onInviteClick, tenant: tenant, currentSpa: currentSPA, hasUserTabPermission: hasUserTabPermission, hasGroupsTabPermission: hasGroupsTabPermission, hasPoliciesTabPermission: hasPoliciesTabPermission, hasRolesTabPermission: hasRolesTabPermission, hasTeamsTabPermission: hasTeamsTabPermission, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, hasAlertsTabPermission: hasAlertsTabPermission, rbacPermissions: rbacPermissions, enableRbac: enableRbac, showGradebookTab: showGradebookTab })), activeTab === 'organization' && (jsx(OrganizationTab, { platformKey: tenant, tenant: tenants.find((ten) => ten.key === tenant), onTenantUpdate: onTenantUpdate, setOrganizationLogoFromOutside: setOrganizationLogo, defaultSupportPhone: defaultSupportPhone, enableSupportPhone: enableSupportPhone })), activeTab === 'integrations' && (jsx(IntegrationsTab, { tenantKey: tenant, username: username })), activeTab === 'billing' && (jsx(BillingTab, { tenant: tenant, userActiveApp: userActiveApp, username: username, currentUserEmail: email, mainPlatformKey: mainPlatformKey })), activeTab === 'monetization' && (jsx(MonetizationTab, { platformKey: tenant, authURL: authURL })), activeTab === 'datasets' && showBenchmarks && (jsx(BenchmarksTab, { tenantKey: tenant, username: username })), activeTab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenant, currentSPA: currentSPA, username: username, authURL: authURL, currentPlatformBaseDomain: currentPlatformBaseDomain }))] })] }), jsx(ToastProvider, {})] }));
|
|
254221
256893
|
}
|
|
254222
256894
|
|
|
254223
256895
|
function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, showMentorAIDisplayCheckbox = false, showLeaderboardDisplayCheckbox = false, showGradebookTab = false, showUsernameField = false, showPlatformName = false, useGravatarPicFallback = true, enableCatalogInvite = false, targetTab = 'basic', currentPlan = '', currentSPA = '', userActiveApp = null, authURL, tenants = [], onTenantUpdate, currentPlatformBaseDomain = '', rbacPermissions = {}, enableRbac = false, onLoadGroupPermissions, onTabChange, defaultSupportPhone = '', onBillingTabRequest, onAccountDeleted, enableMemoryTab = false, showBenchmarks = false, localLLMProps, systemControlProps, enableSupportPhone, }) {
|
|
@@ -257511,141 +260183,6 @@ function NavBar({ username, isAuthenticated = false, onNewChat, onSettings, onLo
|
|
|
257511
260183
|
return (jsxs("header", { className: "flex h-20 shrink-0 items-center gap-2 bg-white px-4 border-b border-[#D0E0FF]", children: [jsx(SidebarTrigger, { className: "-ml-1" }), mentorName && !showMentorDropdown && (jsx("div", { className: "flex items-center gap-2 ml-4", children: jsx("h1", { className: "text-lg font-semibold text-gray-900", children: mentorName }) })), mentorName && showMentorDropdown && (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs(Button$1, { variant: "ghost", className: "gap-2 text-gray-900 hover:bg-gray-100 ml-4", children: [jsx("span", { className: "font-semibold", children: mentorName }), jsx(ChevronDown, { className: "h-4 w-4" })] }) }), jsxs(DropdownMenuContent, { align: "start", className: "w-[200px]", children: [jsx(DropdownMenuItem, { children: t('navbar.switchAgent') }), jsx(DropdownMenuItem, { children: t('navbar.exploreAgents') })] })] })), jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [onNewChat && (jsxs(Button$1, { variant: "ghost", size: "sm", onClick: onNewChat, className: "gap-2 text-[#646464] hover:text-[#484848] hover:bg-transparent", children: [jsx(MessageSquare, { className: "h-4 w-4" }), jsx("span", { className: "hidden sm:inline", children: t('navbar.newChat') })] })), isAuthenticated ? (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs(Button$1, { variant: "ghost", size: "sm", className: "gap-2 text-[#646464] hover:text-[#484848] hover:bg-transparent", children: [jsxs(Avatar, { className: "h-6 w-6", children: [jsx(AvatarImage, { src: `https://api.dicebear.com/7.x/initials/svg?seed=${username}` }), jsx(AvatarFallback, { children: username === null || username === void 0 ? void 0 : username.charAt(0).toUpperCase() })] }), jsx("span", { className: "hidden sm:inline", children: username })] }) }), jsxs(DropdownMenuContent, { align: "end", className: "w-[180px] rounded-md border border-gray-200 bg-white p-2 shadow-lg", children: [additionalMenuItems.map((item, index) => (jsxs(React__default.Fragment, { children: [item.separator && jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuItem, { onClick: item.onClick, className: "flex cursor-pointer items-center rounded-md px-3 py-2 text-sm text-gray-700 hover:bg-gray-100", children: [jsx(item.icon, { className: "mr-3 h-4 w-4 text-gray-600" }), item.label] })] }, item.label))), onSettings && (jsxs(DropdownMenuItem, { onClick: onSettings, className: "flex cursor-pointer items-center rounded-md px-3 py-2 text-sm text-gray-700 hover:bg-gray-100", children: [jsx(Settings$1, { className: "mr-3 h-4 w-4 text-gray-600" }), t('common.settings')] })), onLogout && (jsxs(Fragment$1, { children: [jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuItem, { onClick: onLogout, className: "flex cursor-pointer items-center rounded-md px-3 py-2 text-sm text-gray-700 hover:bg-gray-100", children: [jsx(LogOut, { className: "mr-3 h-4 w-4 text-gray-600" }), t('common.logout')] })] }))] })] })) : (onLogin && (jsxs(Button$1, { onClick: onLogin, size: "sm", className: "gap-2", children: [jsx(LogIn, { className: "h-4 w-4" }), t('common.login')] })))] })] }));
|
|
257512
260184
|
}
|
|
257513
260185
|
|
|
257514
|
-
var COLLAPSIBLE_NAME = "Collapsible";
|
|
257515
|
-
var [createCollapsibleContext] = createContextScope$1(COLLAPSIBLE_NAME);
|
|
257516
|
-
var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
|
|
257517
|
-
var Collapsible$1 = React.forwardRef(
|
|
257518
|
-
(props, forwardedRef) => {
|
|
257519
|
-
const {
|
|
257520
|
-
__scopeCollapsible,
|
|
257521
|
-
open: openProp,
|
|
257522
|
-
defaultOpen,
|
|
257523
|
-
disabled,
|
|
257524
|
-
onOpenChange,
|
|
257525
|
-
...collapsibleProps
|
|
257526
|
-
} = props;
|
|
257527
|
-
const [open, setOpen] = useControllableState$1({
|
|
257528
|
-
prop: openProp,
|
|
257529
|
-
defaultProp: defaultOpen ?? false,
|
|
257530
|
-
onChange: onOpenChange,
|
|
257531
|
-
caller: COLLAPSIBLE_NAME
|
|
257532
|
-
});
|
|
257533
|
-
return /* @__PURE__ */ jsx(
|
|
257534
|
-
CollapsibleProvider,
|
|
257535
|
-
{
|
|
257536
|
-
scope: __scopeCollapsible,
|
|
257537
|
-
disabled,
|
|
257538
|
-
contentId: useId$1(),
|
|
257539
|
-
open,
|
|
257540
|
-
onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
257541
|
-
children: /* @__PURE__ */ jsx(
|
|
257542
|
-
Primitive$2.div,
|
|
257543
|
-
{
|
|
257544
|
-
"data-state": getState(open),
|
|
257545
|
-
"data-disabled": disabled ? "" : void 0,
|
|
257546
|
-
...collapsibleProps,
|
|
257547
|
-
ref: forwardedRef
|
|
257548
|
-
}
|
|
257549
|
-
)
|
|
257550
|
-
}
|
|
257551
|
-
);
|
|
257552
|
-
}
|
|
257553
|
-
);
|
|
257554
|
-
Collapsible$1.displayName = COLLAPSIBLE_NAME;
|
|
257555
|
-
var TRIGGER_NAME$1 = "CollapsibleTrigger";
|
|
257556
|
-
var CollapsibleTrigger$1 = React.forwardRef(
|
|
257557
|
-
(props, forwardedRef) => {
|
|
257558
|
-
const { __scopeCollapsible, ...triggerProps } = props;
|
|
257559
|
-
const context = useCollapsibleContext(TRIGGER_NAME$1, __scopeCollapsible);
|
|
257560
|
-
return /* @__PURE__ */ jsx(
|
|
257561
|
-
Primitive$2.button,
|
|
257562
|
-
{
|
|
257563
|
-
type: "button",
|
|
257564
|
-
"aria-controls": context.contentId,
|
|
257565
|
-
"aria-expanded": context.open || false,
|
|
257566
|
-
"data-state": getState(context.open),
|
|
257567
|
-
"data-disabled": context.disabled ? "" : void 0,
|
|
257568
|
-
disabled: context.disabled,
|
|
257569
|
-
...triggerProps,
|
|
257570
|
-
ref: forwardedRef,
|
|
257571
|
-
onClick: composeEventHandlers$1(props.onClick, context.onOpenToggle)
|
|
257572
|
-
}
|
|
257573
|
-
);
|
|
257574
|
-
}
|
|
257575
|
-
);
|
|
257576
|
-
CollapsibleTrigger$1.displayName = TRIGGER_NAME$1;
|
|
257577
|
-
var CONTENT_NAME$2 = "CollapsibleContent";
|
|
257578
|
-
var CollapsibleContent$1 = React.forwardRef(
|
|
257579
|
-
(props, forwardedRef) => {
|
|
257580
|
-
const { forceMount, ...contentProps } = props;
|
|
257581
|
-
const context = useCollapsibleContext(CONTENT_NAME$2, props.__scopeCollapsible);
|
|
257582
|
-
return /* @__PURE__ */ jsx(Presence$2, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
|
|
257583
|
-
}
|
|
257584
|
-
);
|
|
257585
|
-
CollapsibleContent$1.displayName = CONTENT_NAME$2;
|
|
257586
|
-
var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
|
|
257587
|
-
const { __scopeCollapsible, present, children, ...contentProps } = props;
|
|
257588
|
-
const context = useCollapsibleContext(CONTENT_NAME$2, __scopeCollapsible);
|
|
257589
|
-
const [isPresent, setIsPresent] = React.useState(present);
|
|
257590
|
-
const ref = React.useRef(null);
|
|
257591
|
-
const composedRefs = useComposedRefs$1(forwardedRef, ref);
|
|
257592
|
-
const heightRef = React.useRef(0);
|
|
257593
|
-
const height = heightRef.current;
|
|
257594
|
-
const widthRef = React.useRef(0);
|
|
257595
|
-
const width = widthRef.current;
|
|
257596
|
-
const isOpen = context.open || isPresent;
|
|
257597
|
-
const isMountAnimationPreventedRef = React.useRef(isOpen);
|
|
257598
|
-
const originalStylesRef = React.useRef(void 0);
|
|
257599
|
-
React.useEffect(() => {
|
|
257600
|
-
const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
|
|
257601
|
-
return () => cancelAnimationFrame(rAF);
|
|
257602
|
-
}, []);
|
|
257603
|
-
useLayoutEffect2$1(() => {
|
|
257604
|
-
const node = ref.current;
|
|
257605
|
-
if (node) {
|
|
257606
|
-
originalStylesRef.current = originalStylesRef.current || {
|
|
257607
|
-
transitionDuration: node.style.transitionDuration,
|
|
257608
|
-
animationName: node.style.animationName
|
|
257609
|
-
};
|
|
257610
|
-
node.style.transitionDuration = "0s";
|
|
257611
|
-
node.style.animationName = "none";
|
|
257612
|
-
const rect = node.getBoundingClientRect();
|
|
257613
|
-
heightRef.current = rect.height;
|
|
257614
|
-
widthRef.current = rect.width;
|
|
257615
|
-
if (!isMountAnimationPreventedRef.current) {
|
|
257616
|
-
node.style.transitionDuration = originalStylesRef.current.transitionDuration;
|
|
257617
|
-
node.style.animationName = originalStylesRef.current.animationName;
|
|
257618
|
-
}
|
|
257619
|
-
setIsPresent(present);
|
|
257620
|
-
}
|
|
257621
|
-
}, [context.open, present]);
|
|
257622
|
-
return /* @__PURE__ */ jsx(
|
|
257623
|
-
Primitive$2.div,
|
|
257624
|
-
{
|
|
257625
|
-
"data-state": getState(context.open),
|
|
257626
|
-
"data-disabled": context.disabled ? "" : void 0,
|
|
257627
|
-
id: context.contentId,
|
|
257628
|
-
hidden: !isOpen,
|
|
257629
|
-
...contentProps,
|
|
257630
|
-
ref: composedRefs,
|
|
257631
|
-
style: {
|
|
257632
|
-
[`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
|
|
257633
|
-
[`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
|
|
257634
|
-
...props.style
|
|
257635
|
-
},
|
|
257636
|
-
children: isOpen && children
|
|
257637
|
-
}
|
|
257638
|
-
);
|
|
257639
|
-
});
|
|
257640
|
-
function getState(open) {
|
|
257641
|
-
return open ? "open" : "closed";
|
|
257642
|
-
}
|
|
257643
|
-
var Root$2 = Collapsible$1;
|
|
257644
|
-
|
|
257645
|
-
const Collapsible = Root$2;
|
|
257646
|
-
const CollapsibleTrigger = CollapsibleTrigger$1;
|
|
257647
|
-
const CollapsibleContent = CollapsibleContent$1;
|
|
257648
|
-
|
|
257649
260186
|
// src/arrow.tsx
|
|
257650
260187
|
var NAME$1 = "Arrow";
|
|
257651
260188
|
var Arrow$1 = React.forwardRef((props, forwardedRef) => {
|