From 5cfa7d19c8d431d6a7010e41fa21f748f59f3530 Mon Sep 17 00:00:00 2001
From: Ted <107536598+TedxTed@users.noreply.github.com>
Date: Mon, 13 Jan 2025 10:40:54 +0800
Subject: [PATCH] Merge pull request #612 from
urfit-tech/fix/adjust-ExternalLinkAdminModalBlock-and-ProgramPackageProgramConnectionModal
---
.../program/ExternalLinkAdminModalBlock.tsx | 26 +-
src/components/program/translation.ts | 3 +-
.../ProgramPackageProgramConnectionModal.tsx | 31 +-
src/hasura.d.ts | 311 +++++++++++++++++-
src/helpers/translation.ts | 6 +
src/translations/locales/en-us.json | 4 +-
src/translations/locales/id.json | 4 +-
src/translations/locales/ja.json | 4 +-
src/translations/locales/ko.json | 4 +-
src/translations/locales/vi.json | 4 +-
src/translations/locales/zh-cn.json | 4 +-
src/translations/locales/zh-tw.json | 4 +-
12 files changed, 380 insertions(+), 25 deletions(-)
diff --git a/src/components/program/ExternalLinkAdminModalBlock.tsx b/src/components/program/ExternalLinkAdminModalBlock.tsx
index 20eecd58d..761f92f00 100644
--- a/src/components/program/ExternalLinkAdminModalBlock.tsx
+++ b/src/components/program/ExternalLinkAdminModalBlock.tsx
@@ -10,9 +10,9 @@ import programMessages from './translation'
import { useMutateProgramContent } from '../../hooks/program'
import { handleError } from '../../helpers'
import { useApp } from 'lodestar-app-element/src/contexts/AppContext'
-import dayjs, { Dayjs } from 'dayjs'
import { useState } from 'react'
import { commonMessages } from '../../helpers/translation'
+import moment, { Moment } from 'moment'
type FieldProps = {
title: string
@@ -23,11 +23,11 @@ type FieldProps = {
ratio: number
isOn: boolean
displayMode: DisplayMode
- publishedAt: Dayjs
+ publishedAt: Moment
+ assessmentId: string
}
const linkMap = {
- assessment: 'exam',
accessLinks: 'test',
}
@@ -86,6 +86,11 @@ const ExternalLinkAdminModalBlock: React.FC<{
isNotifyUpdate: false,
pinnedStatus: false,
displayMode: values.displayMode,
+ publishedAt: values.publishedAt
+ ? values.publishedAt.toDate()
+ : values.displayMode !== 'conceal'
+ ? new Date()
+ : null,
},
})
await updateProgramContentBody({
@@ -94,14 +99,9 @@ const ExternalLinkAdminModalBlock: React.FC<{
data: {
trigger: 'externalTestRequirement',
parameters: {
- assessment: values.link.filter(item => item.type === 'exam').map(item => item.url),
+ assessmentId: values.assessmentId,
accessLinks: values.link.filter(item => item.type === 'test').map(item => item.url),
programPackageCompleteRatio: values.ratio,
- publishedAt: values.publishedAt
- ? values.publishedAt.toDate()
- : values.displayMode !== 'conceal'
- ? new Date()
- : null,
},
isOn: values.isOn,
},
@@ -126,10 +126,11 @@ const ExternalLinkAdminModalBlock: React.FC<{
layout="vertical"
initialValues={{
title: programContent.title || '',
+ assessmentId: programContent?.programContentBody?.data?.parameters?.assessmentId,
displayMode: programContent.displayMode,
isOn: programContent?.programContentBody?.data?.isOn ?? true,
ratio: programContent?.programContentBody?.data?.parameters?.programPackageCompleteRatio || 0,
- publishedAt: programContent.publishedAt ? dayjs(programContent.publishedAt) : dayjs().startOf('minute'),
+ publishedAt: programContent.publishedAt ? moment(programContent.publishedAt) : moment().startOf('minute'),
link: transformedData || [{ amount: 1 }],
}}
onFinish={handleSubmit}
@@ -192,7 +193,10 @@ const ExternalLinkAdminModalBlock: React.FC<{
-
+
+
+
+
{(fields, { add, remove }) => (
diff --git a/src/components/program/translation.ts b/src/components/program/translation.ts
index d6725208d..67c3ad2ba 100644
--- a/src/components/program/translation.ts
+++ b/src/components/program/translation.ts
@@ -232,7 +232,8 @@ const programMessages = {
}),
ExternalLinkForm: defineMessages({
title: { id: 'program.ExternalLinkForm.title', defaultMessage: 'Examination paper name' },
- link: { id: 'program.ExternalLinkForm.link', defaultMessage: 'Exam paper/test link' },
+ links: { id: 'program.ExternalLinkForm.links', defaultMessage: 'Test Links' },
+ examLink: { id: 'program.ExternalLinkForm.examLink', defaultMessage: 'Exam Link' },
typeLabel: { id: 'program.ExternalLinkForm.typeLabel', defaultMessage: 'Type' },
linkLabel: { id: 'program.ExternalLinkForm.linkLabel', defaultMessage: 'Link' },
programPackageCompleteRatio: {
diff --git a/src/components/programPackage/ProgramPackageProgramConnectionModal.tsx b/src/components/programPackage/ProgramPackageProgramConnectionModal.tsx
index 66df6bb3d..8121a7c2a 100644
--- a/src/components/programPackage/ProgramPackageProgramConnectionModal.tsx
+++ b/src/components/programPackage/ProgramPackageProgramConnectionModal.tsx
@@ -27,7 +27,8 @@ const ProgramPackageProgramConnectionModal: React.FC<{
const [form] = useForm
()
const { id: appId } = useApp()
const { availablePrograms } = useGetAvailableProgramCollection(appId)
- const { includesBodyTypePrograms } = useGetIncludesBodyTypeProgramCollection(appId, 'link')
+ const { includesBodyTypePrograms, refetch: refetchInCludesBodyTypeProgramCollection } =
+ useGetIncludesBodyTypeProgramCollection(appId, 'link')
const [insertProgramPackageProgram] = useMutation<
hasura.INSERT_PROGRAM_PACKAGE_PROGRAM,
hasura.INSERT_PROGRAM_PACKAGE_PROGRAMVariables
@@ -41,16 +42,23 @@ const ProgramPackageProgramConnectionModal: React.FC<{
.then(() => {
setLoading(true)
const values = form.getFieldsValue()
- const programsId = values.programValues.map((value: string) => value.split('_')[0])
+ const programsIds = values.programValues.map((value: string) => value.split('_')[0])
+ const matchBodyTypeIncludesLinkPrograms = availablePrograms
+ .filter(program => programsIds.includes(program.id))
+ .filter(program => program.programContentBody.includes('link'))
+ if (matchBodyTypeIncludesLinkPrograms.length > 1) {
+ setLoading(false)
+ return window.alert(formatMessage(programPackageMessages.alertMessage.oneLinkError))
+ }
insertProgramPackageProgram({
variables: {
- programs: programsId.map((programId: string, index: number) => ({
+ programs: programsIds.map((programId: string, index: number) => ({
program_package_id: programPackageId,
program_id: programId,
position: index,
})),
delete_program_package_programs_id: programs
- .filter(program => !programsId.includes(program.id))
+ .filter(program => !programsIds.includes(program.id))
.map(program => program.programPackageProgramId),
},
})
@@ -58,6 +66,7 @@ const ProgramPackageProgramConnectionModal: React.FC<{
message.success(formatMessage(commonMessages.event.successfullySaved))
setVisible(false)
onRefetch?.()
+ refetchInCludesBodyTypeProgramCollection()
})
.catch(handleError)
.finally(() => setLoading(false))
@@ -155,6 +164,13 @@ const useGetAvailableProgramCollection = (appId: string) => {
title
is_subscription
published_at
+ program_content_sections {
+ program_contents {
+ program_content_body {
+ type
+ }
+ }
+ }
}
}
`,
@@ -166,6 +182,7 @@ const useGetAvailableProgramCollection = (appId: string) => {
title: string | null
isSubscription: boolean
publishedAt: string
+ programContentBody: (string | null | undefined)[]
}[] =
loading || error || !data
? []
@@ -174,6 +191,9 @@ const useGetAvailableProgramCollection = (appId: string) => {
title: program.title || '',
isSubscription: program.is_subscription,
publishedAt: program.published_at,
+ programContentBody: program.program_content_sections
+ .map(section => section.program_contents.map(content => content.program_content_body.type))
+ .flat(),
}))
return {
loading,
@@ -184,7 +204,7 @@ const useGetAvailableProgramCollection = (appId: string) => {
}
const useGetIncludesBodyTypeProgramCollection = (appId: string, includesBodyType: string) => {
- const { loading, error, data } = useQuery<
+ const { loading, error, data, refetch } = useQuery<
hasura.GetIncludesBodyTypeProgramCollection,
hasura.GetIncludesBodyTypeProgramCollectionVariables
>(
@@ -239,6 +259,7 @@ const useGetIncludesBodyTypeProgramCollection = (appId: string, includesBodyType
return {
includesBodyTypePrograms,
+ refetch,
}
}
diff --git a/src/hasura.d.ts b/src/hasura.d.ts
index 87b274121..73f5dd553 100644
--- a/src/hasura.d.ts
+++ b/src/hasura.d.ts
@@ -31039,6 +31039,188 @@ export type invoice_insert_input = {
updated_at?: InputMaybe;
};
+/** columns and relationships of "invoice_log" */
+export type invoice_log = {
+ __typename?: 'invoice_log';
+ app_invoice_gateway_id: Scalars['uuid'];
+ created_at: Scalars['timestamptz'];
+ merchant_order_no: Scalars['uuid'];
+ message: Scalars['String'];
+ order_id: Scalars['String'];
+ status: Scalars['String'];
+};
+
+/** aggregated selection of "invoice_log" */
+export type invoice_log_aggregate = {
+ __typename?: 'invoice_log_aggregate';
+ aggregate?: Maybe;
+ nodes: Array;
+};
+
+/** aggregate fields of "invoice_log" */
+export type invoice_log_aggregate_fields = {
+ __typename?: 'invoice_log_aggregate_fields';
+ count: Scalars['Int'];
+ max?: Maybe;
+ min?: Maybe;
+};
+
+
+/** aggregate fields of "invoice_log" */
+export type invoice_log_aggregate_fieldscountArgs = {
+ columns?: InputMaybe>;
+ distinct?: InputMaybe;
+};
+
+/** Boolean expression to filter rows from the table "invoice_log". All fields are combined with a logical 'AND'. */
+export type invoice_log_bool_exp = {
+ _and?: InputMaybe>;
+ _not?: InputMaybe;
+ _or?: InputMaybe>;
+ app_invoice_gateway_id?: InputMaybe;
+ created_at?: InputMaybe;
+ merchant_order_no?: InputMaybe;
+ message?: InputMaybe;
+ order_id?: InputMaybe;
+ status?: InputMaybe;
+};
+
+/** unique or primary key constraints on table "invoice_log" */
+export enum invoice_log_constraint {
+ /** unique or primary key constraint on columns "merchant_order_no" */
+ invoice_log_pkey = 'invoice_log_pkey'
+}
+
+/** input type for inserting data into table "invoice_log" */
+export type invoice_log_insert_input = {
+ app_invoice_gateway_id?: InputMaybe;
+ created_at?: InputMaybe;
+ merchant_order_no?: InputMaybe;
+ message?: InputMaybe;
+ order_id?: InputMaybe;
+ status?: InputMaybe;
+};
+
+/** aggregate max on columns */
+export type invoice_log_max_fields = {
+ __typename?: 'invoice_log_max_fields';
+ app_invoice_gateway_id?: Maybe;
+ created_at?: Maybe;
+ merchant_order_no?: Maybe;
+ message?: Maybe;
+ order_id?: Maybe;
+ status?: Maybe;
+};
+
+/** aggregate min on columns */
+export type invoice_log_min_fields = {
+ __typename?: 'invoice_log_min_fields';
+ app_invoice_gateway_id?: Maybe;
+ created_at?: Maybe;
+ merchant_order_no?: Maybe;
+ message?: Maybe;
+ order_id?: Maybe;
+ status?: Maybe;
+};
+
+/** response of any mutation on the table "invoice_log" */
+export type invoice_log_mutation_response = {
+ __typename?: 'invoice_log_mutation_response';
+ /** number of rows affected by the mutation */
+ affected_rows: Scalars['Int'];
+ /** data from the rows affected by the mutation */
+ returning: Array;
+};
+
+/** on_conflict condition type for table "invoice_log" */
+export type invoice_log_on_conflict = {
+ constraint: invoice_log_constraint;
+ update_columns?: Array;
+ where?: InputMaybe;
+};
+
+/** Ordering options when selecting data from "invoice_log". */
+export type invoice_log_order_by = {
+ app_invoice_gateway_id?: InputMaybe;
+ created_at?: InputMaybe;
+ merchant_order_no?: InputMaybe;
+ message?: InputMaybe;
+ order_id?: InputMaybe;
+ status?: InputMaybe;
+};
+
+/** primary key columns input for table: invoice_log */
+export type invoice_log_pk_columns_input = {
+ merchant_order_no: Scalars['uuid'];
+};
+
+/** select columns of table "invoice_log" */
+export enum invoice_log_select_column {
+ /** column name */
+ app_invoice_gateway_id = 'app_invoice_gateway_id',
+ /** column name */
+ created_at = 'created_at',
+ /** column name */
+ merchant_order_no = 'merchant_order_no',
+ /** column name */
+ message = 'message',
+ /** column name */
+ order_id = 'order_id',
+ /** column name */
+ status = 'status'
+}
+
+/** input type for updating data in table "invoice_log" */
+export type invoice_log_set_input = {
+ app_invoice_gateway_id?: InputMaybe;
+ created_at?: InputMaybe;
+ merchant_order_no?: InputMaybe;
+ message?: InputMaybe;
+ order_id?: InputMaybe;
+ status?: InputMaybe;
+};
+
+/** Streaming cursor of the table "invoice_log" */
+export type invoice_log_stream_cursor_input = {
+ /** Stream column input with initial value */
+ initial_value: invoice_log_stream_cursor_value_input;
+ /** cursor ordering */
+ ordering?: InputMaybe;
+};
+
+/** Initial value of the column from where the streaming should start */
+export type invoice_log_stream_cursor_value_input = {
+ app_invoice_gateway_id?: InputMaybe;
+ created_at?: InputMaybe;
+ merchant_order_no?: InputMaybe;
+ message?: InputMaybe;
+ order_id?: InputMaybe;
+ status?: InputMaybe;
+};
+
+/** update columns of table "invoice_log" */
+export enum invoice_log_update_column {
+ /** column name */
+ app_invoice_gateway_id = 'app_invoice_gateway_id',
+ /** column name */
+ created_at = 'created_at',
+ /** column name */
+ merchant_order_no = 'merchant_order_no',
+ /** column name */
+ message = 'message',
+ /** column name */
+ order_id = 'order_id',
+ /** column name */
+ status = 'status'
+}
+
+export type invoice_log_updates = {
+ /** sets the columns of the filtered rows to the given values */
+ _set?: InputMaybe;
+ /** filter the rows which have to be updated */
+ where: invoice_log_bool_exp;
+};
+
/** aggregate max on columns */
export type invoice_max_fields = {
__typename?: 'invoice_max_fields';
@@ -50604,6 +50786,10 @@ export type mutation_root = {
delete_invoice_gateway?: Maybe;
/** delete single row from the table: "invoice_gateway" */
delete_invoice_gateway_by_pk?: Maybe;
+ /** delete data from the table: "invoice_log" */
+ delete_invoice_log?: Maybe;
+ /** delete single row from the table: "invoice_log" */
+ delete_invoice_log_by_pk?: Maybe;
/** delete data from the table: "issue" */
delete_issue?: Maybe;
/** delete single row from the table: "issue" */
@@ -51694,6 +51880,10 @@ export type mutation_root = {
insert_invoice_gateway?: Maybe;
/** insert a single row into the table: "invoice_gateway" */
insert_invoice_gateway_one?: Maybe;
+ /** insert data into the table: "invoice_log" */
+ insert_invoice_log?: Maybe;
+ /** insert a single row into the table: "invoice_log" */
+ insert_invoice_log_one?: Maybe;
/** insert a single row into the table: "invoice" */
insert_invoice_one?: Maybe;
/** insert data into the table: "issue" */
@@ -52970,6 +53160,12 @@ export type mutation_root = {
update_invoice_gateway_by_pk?: Maybe;
/** update multiples rows of table: "invoice_gateway" */
update_invoice_gateway_many?: Maybe>>;
+ /** update data of the table: "invoice_log" */
+ update_invoice_log?: Maybe;
+ /** update single row of the table: "invoice_log" */
+ update_invoice_log_by_pk?: Maybe;
+ /** update multiples rows of table: "invoice_log" */
+ update_invoice_log_many?: Maybe>>;
/** update multiples rows of table: "invoice" */
update_invoice_many?: Maybe>>;
/** update data of the table: "issue" */
@@ -55106,6 +55302,18 @@ export type mutation_rootdelete_invoice_gateway_by_pkArgs = {
};
+/** mutation root */
+export type mutation_rootdelete_invoice_logArgs = {
+ where: invoice_log_bool_exp;
+};
+
+
+/** mutation root */
+export type mutation_rootdelete_invoice_log_by_pkArgs = {
+ merchant_order_no: Scalars['uuid'];
+};
+
+
/** mutation root */
export type mutation_rootdelete_issueArgs = {
where: issue_bool_exp;
@@ -58536,6 +58744,20 @@ export type mutation_rootinsert_invoice_gateway_oneArgs = {
};
+/** mutation root */
+export type mutation_rootinsert_invoice_logArgs = {
+ objects: Array;
+ on_conflict?: InputMaybe;
+};
+
+
+/** mutation root */
+export type mutation_rootinsert_invoice_log_oneArgs = {
+ object: invoice_log_insert_input;
+ on_conflict?: InputMaybe;
+};
+
+
/** mutation root */
export type mutation_rootinsert_invoice_oneArgs = {
object: invoice_insert_input;
@@ -63233,6 +63455,26 @@ export type mutation_rootupdate_invoice_gateway_manyArgs = {
};
+/** mutation root */
+export type mutation_rootupdate_invoice_logArgs = {
+ _set?: InputMaybe;
+ where: invoice_log_bool_exp;
+};
+
+
+/** mutation root */
+export type mutation_rootupdate_invoice_log_by_pkArgs = {
+ _set?: InputMaybe;
+ pk_columns: invoice_log_pk_columns_input;
+};
+
+
+/** mutation root */
+export type mutation_rootupdate_invoice_log_manyArgs = {
+ updates: Array;
+};
+
+
/** mutation root */
export type mutation_rootupdate_invoice_manyArgs = {
updates: Array;
@@ -111255,6 +111497,12 @@ export type query_root = {
invoice_gateway_aggregate: invoice_gateway_aggregate;
/** fetch data from the table: "invoice_gateway" using primary key columns */
invoice_gateway_by_pk?: Maybe;
+ /** fetch data from the table: "invoice_log" */
+ invoice_log: Array;
+ /** fetch aggregated fields from the table: "invoice_log" */
+ invoice_log_aggregate: invoice_log_aggregate;
+ /** fetch data from the table: "invoice_log" using primary key columns */
+ invoice_log_by_pk?: Maybe;
/** fetch data from the table: "issue" */
issue: Array;
/** fetch aggregated fields from the table: "issue" */
@@ -114969,6 +115217,29 @@ export type query_rootinvoice_gateway_by_pkArgs = {
};
+export type query_rootinvoice_logArgs = {
+ distinct_on?: InputMaybe>;
+ limit?: InputMaybe;
+ offset?: InputMaybe;
+ order_by?: InputMaybe>;
+ where?: InputMaybe;
+};
+
+
+export type query_rootinvoice_log_aggregateArgs = {
+ distinct_on?: InputMaybe>;
+ limit?: InputMaybe;
+ offset?: InputMaybe;
+ order_by?: InputMaybe>;
+ where?: InputMaybe;
+};
+
+
+export type query_rootinvoice_log_by_pkArgs = {
+ merchant_order_no: Scalars['uuid'];
+};
+
+
export type query_rootissueArgs = {
distinct_on?: InputMaybe>;
limit?: InputMaybe;
@@ -128593,6 +128864,14 @@ export type subscription_root = {
invoice_gateway_by_pk?: Maybe;
/** fetch data from the table in a streaming manner: "invoice_gateway" */
invoice_gateway_stream: Array;
+ /** fetch data from the table: "invoice_log" */
+ invoice_log: Array;
+ /** fetch aggregated fields from the table: "invoice_log" */
+ invoice_log_aggregate: invoice_log_aggregate;
+ /** fetch data from the table: "invoice_log" using primary key columns */
+ invoice_log_by_pk?: Maybe;
+ /** fetch data from the table in a streaming manner: "invoice_log" */
+ invoice_log_stream: Array;
/** fetch data from the table in a streaming manner: "invoice" */
invoice_stream: Array;
/** fetch data from the table: "issue" */
@@ -133566,6 +133845,36 @@ export type subscription_rootinvoice_gateway_streamArgs = {
};
+export type subscription_rootinvoice_logArgs = {
+ distinct_on?: InputMaybe>;
+ limit?: InputMaybe;
+ offset?: InputMaybe;
+ order_by?: InputMaybe>;
+ where?: InputMaybe;
+};
+
+
+export type subscription_rootinvoice_log_aggregateArgs = {
+ distinct_on?: InputMaybe>;
+ limit?: InputMaybe;
+ offset?: InputMaybe;
+ order_by?: InputMaybe>;
+ where?: InputMaybe;
+};
+
+
+export type subscription_rootinvoice_log_by_pkArgs = {
+ merchant_order_no: Scalars['uuid'];
+};
+
+
+export type subscription_rootinvoice_log_streamArgs = {
+ batch_size: Scalars['Int'];
+ cursor: Array>;
+ where?: InputMaybe;
+};
+
+
export type subscription_rootinvoice_streamArgs = {
batch_size: Scalars['Int'];
cursor: Array>;
@@ -156926,7 +157235,7 @@ export type GetAvailableProgramCollectionVariables = Exact<{
}>;
-export type GetAvailableProgramCollection = { __typename?: 'query_root', program: Array<{ __typename?: 'program', id: any, title: string, is_subscription: boolean, published_at?: any | null }> };
+export type GetAvailableProgramCollection = { __typename?: 'query_root', program: Array<{ __typename?: 'program', id: any, title: string, is_subscription: boolean, published_at?: any | null, program_content_sections: Array<{ __typename?: 'program_content_section', program_contents: Array<{ __typename?: 'program_content', program_content_body: { __typename?: 'program_content_body', type?: string | null } }> }> }> };
export type GetIncludesBodyTypeProgramCollectionVariables = Exact<{
appId: Scalars['String'];
diff --git a/src/helpers/translation.ts b/src/helpers/translation.ts
index c424cd1a9..d9c39dd96 100644
--- a/src/helpers/translation.ts
+++ b/src/helpers/translation.ts
@@ -897,6 +897,12 @@ export const programPackageMessages = {
defaultMessage: '所有訂閱課程',
},
}),
+ alertMessage: defineMessages({
+ oneLinkError: {
+ id: 'programPackage.alertMessage.oneLinkError',
+ defaultMessage: 'Courses that can only have one external test, please reconfirm.',
+ },
+ }),
}
export const podcastMessages = {
diff --git a/src/translations/locales/en-us.json b/src/translations/locales/en-us.json
index 0f8076308..e1d1b4ec6 100644
--- a/src/translations/locales/en-us.json
+++ b/src/translations/locales/en-us.json
@@ -2602,7 +2602,8 @@
"program.*.on": "ON",
"program.*.off": "OFF",
"program.ExternalLinkForm.title": "Examination paper name",
- "program.ExternalLinkForm.link": "Exam paper/test link",
+ "program.ExternalLinkForm.links": "Test link",
+ "program.ExternalLinkForm.examLink": "Exam Link",
"program.ExternalLinkForm.typeLabel": "Type",
"program.ExternalLinkForm.linkLabel": "Link",
"program.ExternalLinkForm.programPackageCompleteRatio": "Eligibility for admission (view the total progress of the course)",
@@ -2810,6 +2811,7 @@
"program.ui.uploadCaptions": "Upload Captions",
"program.ui.uploadExample": "Example material",
"program.ui.uploadedCaptions": "Uploaded Captions",
+ "programPackage.alertMessage.oneLinkError": "Courses that can only have one external test, please reconfirm",
"programPackage.ProgramPackageBasicFrom.tag": "Tag",
"programPackage.ProgramPackagePlanAdminModal.productLevel": "Plan Level",
"programPackage.ProgramPackagePlanCollectionBlock.editPlan": "Edit Plan",
diff --git a/src/translations/locales/id.json b/src/translations/locales/id.json
index 08cda3651..18fdf846a 100644
--- a/src/translations/locales/id.json
+++ b/src/translations/locales/id.json
@@ -2602,7 +2602,8 @@
"program.*.on": "Aktifkan",
"program.*.off": "mati",
"program.ExternalLinkForm.title": "Nama kertas ujian",
- "program.ExternalLinkForm.link": "Kertas ujian/tautan ujian",
+ "program.ExternalLinkForm.links": "Tautan Ujian",
+ "program.ExternalLinkForm.examLink": "Tautan ujian",
"program.ExternalLinkForm.typeLabel": "ketik",
"program.ExternalLinkForm.linkLabel": "Tautan",
"program.ExternalLinkForm.programPackageCompleteRatio": "Kelayakan untuk masuk (melihat kemajuan total kursus)",
@@ -2810,6 +2811,7 @@
"program.ui.uploadCaptions": "Unggah Teks",
"program.ui.uploadExample": "Contoh materi",
"program.ui.uploadedCaptions": "Teks yang Diunggah",
+ "programPackage.alertMessage.oneLinkError": "Hanya ada satu kursus pengujian eksternal, harap konfirmasi ulang",
"programPackage.ProgramPackageBasicFrom.tag": "menandai",
"programPackage.ProgramPackagePlanAdminModal.productLevel": "Tingkat paket",
"programPackage.ProgramPackagePlanCollectionBlock.editPlan": "Edit paket",
diff --git a/src/translations/locales/ja.json b/src/translations/locales/ja.json
index cce466542..cf018ef22 100644
--- a/src/translations/locales/ja.json
+++ b/src/translations/locales/ja.json
@@ -2588,7 +2588,8 @@
"program.*.on": "有効",
"program.*.off": "オフ",
"program.ExternalLinkForm.title": "審査用紙名",
- "program.ExternalLinkForm.link": "試験用紙/テストリンク",
+ "program.ExternalLinkForm.links": "テストリンク",
+ "program.ExternalLinkForm.examLink": "試験リンク",
"program.ExternalLinkForm.typeLabel": "タイプ",
"program.ExternalLinkForm.linkLabel": "リンク",
"program.ExternalLinkForm.programPackageCompleteRatio": "入学資格 (コースの合計進捗状況を表示)",
@@ -2796,6 +2797,7 @@
"program.ui.uploadCaptions": "字幕をアップロード",
"program.ui.uploadExample": "サンプル素材",
"program.ui.uploadedCaptions": "アップロード済みの字幕",
+ "programPackage.alertMessage.oneLinkError": "外部テスト コースは 1 つだけです。再確認してください。",
"programPackage.ProgramPackageBasicFrom.tag": "タグ",
"programPackage.ProgramPackagePlanAdminModal.productLevel": "プランレベル",
"programPackage.ProgramPackagePlanCollectionBlock.editPlan": "プランを編集",
diff --git a/src/translations/locales/ko.json b/src/translations/locales/ko.json
index f8a6a75cf..6f9ca877b 100644
--- a/src/translations/locales/ko.json
+++ b/src/translations/locales/ko.json
@@ -2588,7 +2588,8 @@
"program.*.on": "활성화",
"program.*.off": "오프",
"program.ExternalLinkForm.title": "시험지 이름",
- "program.ExternalLinkForm.link": "시험지/테스트 링크",
+ "program.ExternalLinkForm.links": "테스트 링크",
+ "program.ExternalLinkForm.examLink": "시험 링크",
"program.ExternalLinkForm.typeLabel": "유형",
"program.ExternalLinkForm.linkLabel": "링크",
"program.ExternalLinkForm.programPackageCompleteRatio": "입학 자격(과정의 전체 진행 상황 보기)",
@@ -2796,6 +2797,7 @@
"program.ui.uploadCaptions": "자막 업로드",
"program.ui.uploadExample": "예제 자료",
"program.ui.uploadedCaptions": "업로드된 자막",
+ "programPackage.alertMessage.oneLinkError": "외부 테스트 과정은 하나만 있을 수 있습니다. 다시 확인하세요.",
"programPackage.ProgramPackageBasicFrom.tag": "태그",
"programPackage.ProgramPackagePlanAdminModal.productLevel": "플랜 레벨",
"programPackage.ProgramPackagePlanCollectionBlock.editPlan": "플랜 편집",
diff --git a/src/translations/locales/vi.json b/src/translations/locales/vi.json
index dbbf04876..a783c4fd1 100644
--- a/src/translations/locales/vi.json
+++ b/src/translations/locales/vi.json
@@ -2602,7 +2602,8 @@
"program.*.on": "Kích hoạt",
"program.*.off": "tắt",
"program.ExternalLinkForm.title": "Tên bài thi",
- "program.ExternalLinkForm.link": "Bài thi/liên kết bài kiểm tra",
+ "program.ExternalLinkForm.links": "liên kết bài kiểm tra",
+ "program.ExternalLinkForm.examLink": "Liên kết bài kiểm tra",
"program.ExternalLinkForm.typeLabel": "loại",
"program.ExternalLinkForm.linkLabel": "Liên kết",
"program.ExternalLinkForm.programPackageCompleteRatio": "Điều kiện nhập học (xem tổng tiến độ của khóa học)",
@@ -2810,6 +2811,7 @@
"program.ui.uploadCaptions": " Tải lên Phụ đề ",
"program.ui.uploadExample": " Tài liệu mẫu ",
"program.ui.uploadedCaptions": " Phụ đề đã Tải lên ",
+ "programPackage.alertMessage.oneLinkError": "Chỉ có thể có một khóa học kiểm tra bên ngoài, vui lòng xác nhận lại",
"programPackage.ProgramPackageBasicFrom.tag": "Nhãn mác",
"programPackage.ProgramPackagePlanAdminModal.productLevel": "Cấp độ gói",
"programPackage.ProgramPackagePlanCollectionBlock.editPlan": "Chỉnh sửa gói",
diff --git a/src/translations/locales/zh-cn.json b/src/translations/locales/zh-cn.json
index a4eaf39db..c431a695a 100644
--- a/src/translations/locales/zh-cn.json
+++ b/src/translations/locales/zh-cn.json
@@ -2602,7 +2602,8 @@
"program.*.on": "启用",
"program.*.off": "关闭",
"program.ExternalLinkForm.title": "考卷名称",
- "program.ExternalLinkForm.link": "考卷/测验连结",
+ "program.ExternalLinkForm.links": "测验连结",
+ "program.ExternalLinkForm.examLink": "考卷连结",
"program.ExternalLinkForm.typeLabel": "类型",
"program.ExternalLinkForm.linkLabel": "连结",
"program.ExternalLinkForm.programPackageCompleteRatio": "准考资格(观看课程总进度)",
@@ -2810,6 +2811,7 @@
"program.ui.uploadCaptions": "上传字幕",
"program.ui.uploadExample": "示例素材",
"program.ui.uploadedCaptions": "已上传字幕",
+ "programPackage.alertMessage.oneLinkError": "仅能有一个外部测验的课程,请重新确认",
"programPackage.ProgramPackageBasicFrom.tag": "标签",
"programPackage.ProgramPackagePlanAdminModal.productLevel": "方案等级",
"programPackage.ProgramPackagePlanCollectionBlock.editPlan": "编辑方案",
diff --git a/src/translations/locales/zh-tw.json b/src/translations/locales/zh-tw.json
index d496818ef..51d0caa5e 100644
--- a/src/translations/locales/zh-tw.json
+++ b/src/translations/locales/zh-tw.json
@@ -2602,7 +2602,8 @@
"program.*.on": "啟用",
"program.*.off": "關閉",
"program.ExternalLinkForm.title": "考卷名稱",
- "program.ExternalLinkForm.link": "考卷/測驗連結",
+ "program.ExternalLinkForm.links": "測驗連結",
+ "program.ExternalLinkForm.examLink": "考卷連結",
"program.ExternalLinkForm.typeLabel": "類型",
"program.ExternalLinkForm.linkLabel": "連結",
"program.ExternalLinkForm.programPackageCompleteRatio": "准考資格(觀看課程總進度)",
@@ -2810,6 +2811,7 @@
"program.ui.uploadCaptions": "上傳字幕",
"program.ui.uploadExample": "範例素材",
"program.ui.uploadedCaptions": "已上傳字幕",
+ "programPackage.alertMessage.oneLinkError": "僅能有一個外部測驗的課程,請重新確認",
"programPackage.ProgramPackageBasicFrom.tag": "標籤",
"programPackage.ProgramPackagePlanAdminModal.productLevel": "方案等級",
"programPackage.ProgramPackagePlanCollectionBlock.editPlan": "編輯方案",