fix: save regenerated course link on generate (#904)

* improvement: save regenerated course link on generate

* improvements: remove disabled prop from "new course link generate" btn as loading prop already serves; added console error logs for uncaught errs during save or new link generation

---------

Co-authored-by: richarddkenneth <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
This commit is contained in:
Richard Kenneth
2026-08-18 12:00:52 +01:00
committed by GitHub
co-authored by richarddkenneth Rotimi Best
parent fcbf38f6a8
commit 41a77ff025
13 changed files with 131 additions and 66 deletions
@@ -51,6 +51,7 @@
let { hasUnsavedChanges = $bindable(false) }: Props = $props();
let isLoading = $state(false);
let isGeneratingLink = $state(false);
let isDeleting = $state(false);
let openCertificateDeadlineDialog = $state(false);
let completionDeadlineTrigger = $state(0);
@@ -66,6 +67,7 @@
let selectedTagIds = $state<string[]>([]);
let initialTagIds = $state<string[]>([]);
let loadedCourseTagsForId = $state<string | null>(null);
let initializedCourseId = $state<string | null>(null);
let isTagPopoverOpen = $state(false);
function normalizeTagIds(tagIds: string[]) {
@@ -285,14 +287,29 @@
hasUnsavedChanges = false;
}
} catch (error) {
console.error(error);
snackbar.error();
}
}
const generateNewCourseLink = () => {
if (!courseApi.course) return;
courseApi.course.slug = generateSlug(courseApi.course.title, { appendTimestamp: true });
hasUnsavedChanges = true;
const generateNewCourseLink = async () => {
if (!courseApi.course || isGeneratingLink) return;
isGeneratingLink = true;
try {
const newSlug = generateSlug(courseApi.course.title, { appendTimestamp: true });
const response = await courseApi.update(courseApi.course.id, { slug: newSlug }, { showSuccessToast: false });
if (courseApi.success && response) {
courseApi.course.slug = response.slug ?? newSlug;
snackbar.success('snackbar.course_settings.success.link_generated');
}
} catch (error) {
console.error(error);
snackbar.error();
} finally {
isGeneratingLink = false;
}
};
async function setDefault(course: Course) {
@@ -389,8 +406,10 @@
});
$effect(() => {
if (courseApi.course) {
setDefault(courseApi.course);
const course = courseApi.course;
if (course?.id && initializedCourseId !== course.id) {
initializedCourseId = course.id;
setDefault(course);
}
});
@@ -590,10 +609,19 @@
>{$t('course.navItem.settings.link')}
<div class="flex items-center gap-1">
<IconButton onclick={generateNewCourseLink}>
<IconButton
onclick={generateNewCourseLink}
loading={isGeneratingLink}
tooltip={$t('course.navItem.settings.generate_link')}
>
<RotateCcwIcon size={16} />
</IconButton>
<IconButton href={courseLink} target="_blank">
<IconButton
href={courseLink}
target="_blank"
disabled={isGeneratingLink || !courseApi.course?.slug}
tooltip={$t('course.navItem.settings.open_link')}
>
<ArrowUpRightIcon size={16} />
</IconButton>
</div>
@@ -606,6 +634,8 @@
onclick={() => {
copyToClipboard(courseLink);
}}
disabled={isGeneratingLink}
tooltip={$t('course.navItem.settings.copy_link')}
>
<Copy size={16} />
</IconButton>
@@ -295,7 +295,8 @@
"unlocked_all_content": "Alle kursusartikler er låst op",
"all_content_already_unlocked": "Alt indhold er allerede låst op",
"content_grouping_enabled": "Indholdsgruppering aktiveret",
"content_grouping_disabled": "Indholdsgruppering deaktiveret"
"content_grouping_disabled": "Indholdsgruppering deaktiveret",
"link_generated": "Kursuslinket blev opdateret"
}
},
"people": {
@@ -859,6 +860,9 @@
"description_live": "Låste lektioner og øvelser forbliver skjult for eleverne, indtil du låser dem op - nyttigt, hvis du udgiver indhold uge for uge.",
"unlock_all": "Lås alle op"
},
"generate_link": "Generer nyt link",
"open_link": "Åbn link i ny fane",
"copy_link": "Kopiér link",
"certificate_deadline_required": "Indstil certifikatfrist først",
"certificate_deadline_required_description": "Overholdelseskurser kræver en certifikatfrist, før elever kan tilmeldes. Indstil venligst en deadline i kursusindstillingerne før udgivelse.",
"go_to_completion_deadline": "Gå til fuldførelsesfristen",
@@ -295,7 +295,8 @@
"unlocked_all_content": "Alle Kursgegenstände freigeschaltet",
"all_content_already_unlocked": "Alle Inhalte sind bereits freigeschaltet",
"content_grouping_enabled": "Inhaltsgruppierung aktiviert",
"content_grouping_disabled": "Inhaltsgruppierung deaktiviert"
"content_grouping_disabled": "Inhaltsgruppierung deaktiviert",
"link_generated": "Der Kurslink wurde erfolgreich aktualisiert"
}
},
"people": {
@@ -859,6 +860,9 @@
"description_live": "Gesperrte Lektionen und Übungen bleiben den Schülern verborgen, bis Sie sie entsperren – nützlich, wenn Sie Inhalte Woche für Woche veröffentlichen.",
"unlock_all": "Schalte alle frei"
},
"generate_link": "Neuen Link generieren",
"open_link": "Link in neuem Tab öffnen",
"copy_link": "Link kopieren",
"certificate_deadline_required": "Legen Sie zuerst die Frist für das Zertifikat fest",
"certificate_deadline_required_description": "Für Compliance-Kurse ist eine Zertifikatsfrist erforderlich, bevor Lernende eingeschrieben werden können. Bitte legen Sie vor der Veröffentlichung eine Frist in den Kurseinstellungen fest.",
"go_to_completion_deadline": "Gehen Sie zur Abschlussfrist",
@@ -3623,6 +3623,9 @@
"setup_landing_for_link": "Setup landing page to get course link",
"coming_soon": "Coming soon",
"link": "Course Link",
"generate_link": "Generate new link",
"open_link": "Open link in new tab",
"copy_link": "Copy link",
"grading": "Grading",
"reports": "Make grading reports available for lessons",
"disabled": "Disabled",
@@ -4251,7 +4254,8 @@
"saved": "Saved successfully",
"successful_integration": "Integration successful",
"successful_deletion": "Integration deleted",
"update_successful": "Update successful"
"update_successful": "Update successful",
"link_generated": "Course link updated successfully"
},
"error": {
"not_right": "Something's not right - Please try later",
@@ -295,7 +295,8 @@
"unlocked_all_content": "Todos los elementos del curso desbloqueados",
"all_content_already_unlocked": "Todo el contenido ya está desbloqueado",
"content_grouping_enabled": "Agrupación de contenidos activada",
"content_grouping_disabled": "Agrupación de contenidos desactivada"
"content_grouping_disabled": "Agrupación de contenidos desactivada",
"link_generated": "Enlace del curso actualizado exitosamente"
}
},
"people": {
@@ -859,6 +860,9 @@
"description_live": "Las lecciones y ejercicios bloqueados permanecen ocultos para los estudiantes hasta que los desbloqueas, lo que resulta útil si publicas contenido semana tras semana.",
"unlock_all": "Desbloquear todo"
},
"generate_link": "Generar nuevo enlace",
"open_link": "Abrir enlace en nueva pestaña",
"copy_link": "Copiar enlace",
"certificate_deadline_required": "Establecer la fecha límite del certificado primero",
"certificate_deadline_required_description": "Los cursos de cumplimiento requieren una fecha límite para el certificado antes de que los estudiantes puedan inscribirse. Establezca una fecha límite en la configuración del curso antes de publicarlo.",
"go_to_completion_deadline": "Ir a la fecha límite de finalización",
@@ -295,7 +295,8 @@
"unlocked_all_content": "Tous les éléments de cours débloqués",
"all_content_already_unlocked": "Tout le contenu est déjà déverrouillé",
"content_grouping_enabled": "Regroupement de contenu activé",
"content_grouping_disabled": "Regroupement de contenu désactivé"
"content_grouping_disabled": "Regroupement de contenu désactivé",
"link_generated": "Lien du cours mis à jour avec succès"
}
},
"people": {
@@ -859,6 +860,9 @@
"description_live": "Les leçons et les exercices verrouillés restent cachés aux étudiants jusqu'à ce que vous les déverrouilliez, ce qui est utile si vous publiez du contenu semaine après semaine.",
"unlock_all": "Débloquez tout"
},
"generate_link": "Générer un nouveau lien",
"open_link": "Ouvrir le lien dans un nouvel onglet",
"copy_link": "Copier le lien",
"certificate_deadline_required": "Définir d'abord la date limite du certificat",
"certificate_deadline_required_description": "Les cours de conformité nécessitent une date limite de certificat avant que les apprenants puissent être inscrits. Veuillez définir une date limite dans les paramètres du cours avant de publier.",
"go_to_completion_deadline": "Accéder à la date limite d'achèvement",
@@ -295,7 +295,8 @@
"unlocked_all_content": "सभी पाठ्यक्रम आइटम अनलॉक हो गए",
"all_content_already_unlocked": "सभी सामग्री पहले से अनलॉक है",
"content_grouping_enabled": "सामग्री समूहीकरण सक्षम",
"content_grouping_disabled": "सामग्री समूहीकरण अक्षम"
"content_grouping_disabled": "सामग्री समूहीकरण अक्षम",
"link_generated": "पाठ्यक्रम लिंक सफलतापूर्वक अपडेट किया गया"
}
},
"people": {
@@ -860,6 +861,9 @@
"unlock_all": "सबको अनलॉक करें"
},
"certificate_deadline_required": "पहले प्रमाणपत्र की समय सीमा निर्धारित करें",
"generate_link": "नया लिंक जनरेट करें",
"open_link": "नए टैब में लिंक खोलें",
"copy_link": "लिंक कॉपी करें",
"certificate_deadline_required_description": "अनुपालन पाठ्यक्रमों में शिक्षार्थियों के नामांकन से पहले प्रमाणपत्र की समय सीमा की आवश्यकता होती है। कृपया प्रकाशन से पहले पाठ्यक्रम सेटिंग में एक समय सीमा निर्धारित करें।",
"go_to_completion_deadline": "समापन समय सीमा पर जाएं",
"completion_deadline_label": "कोर्स पूरा करने की समय सीमा",
@@ -295,7 +295,8 @@
"unlocked_all_content": "Wszystkie elementy kursu odblokowane",
"all_content_already_unlocked": "Cała treść jest już odblokowana",
"content_grouping_enabled": "Grupowanie treści włączone",
"content_grouping_disabled": "Grupowanie treści wyłączone"
"content_grouping_disabled": "Grupowanie treści wyłączone",
"link_generated": "Link do kursu został pomyślnie zaktualizowany"
}
},
"people": {
@@ -860,6 +861,9 @@
"unlock_all": "Odblokuj wszystko"
},
"certificate_deadline_required": "Najpierw ustal termin wydania certyfikatu",
"generate_link": "Wygeneruj nowy link",
"open_link": "Otwórz link w nowej karcie",
"copy_link": "Skopiuj link",
"certificate_deadline_required_description": "Kursy zgodności wymagają wystawienia certyfikatu przed zapisaniem się na nie uczestników. Przed publikacją ustaw termin w ustawieniach kursu.",
"go_to_completion_deadline": "Przejdź do terminu ukończenia",
"completion_deadline_label": "Termin zakończenia kursu",
@@ -295,7 +295,8 @@
"unlocked_all_content": "Todos os itens do curso desbloqueados",
"all_content_already_unlocked": "Todo o conteúdo já está desbloqueado",
"content_grouping_enabled": "Agrupamento de conteúdo ativado",
"content_grouping_disabled": "Agrupamento de conteúdo desativado"
"content_grouping_disabled": "Agrupamento de conteúdo desativado",
"link_generated": "Link do curso atualizado com sucesso"
}
},
"people": {
@@ -859,6 +860,9 @@
"description_live": "Aulas e exercícios bloqueados permanecem ocultos aos alunos até que você os desbloqueie – o que é útil se você liberar conteúdo semana após semana.",
"unlock_all": "Desbloquear tudo"
},
"generate_link": "Gerar novo link",
"open_link": "Abrir link em nova aba",
"copy_link": "Copiar link",
"certificate_deadline_required": "Defina o prazo do certificado primeiro",
"certificate_deadline_required_description": "Os cursos de conformidade exigem um prazo de certificação antes que os alunos possam ser matriculados. Defina um prazo nas configurações do curso antes de publicar.",
"go_to_completion_deadline": "Ir para o prazo de conclusão",
@@ -295,7 +295,8 @@
"unlocked_all_content": "Все предметы курса разблокированы",
"all_content_already_unlocked": "Весь контент уже разблокирован",
"content_grouping_enabled": "Группировка контента включена",
"content_grouping_disabled": "Группировка контента отключена"
"content_grouping_disabled": "Группировка контента отключена",
"link_generated": "Ссылка на курс успешно обновлена"
}
},
"people": {
@@ -859,6 +860,9 @@
"description_live": "Заблокированные уроки и упражнения остаются скрытыми от учащихся, пока вы их не разблокируете. Это полезно, если вы выпускаете контент еженедельно.",
"unlock_all": "Разблокировать все"
},
"generate_link": "Создать новую ссылку",
"open_link": "Открыть ссылку в новой вкладке",
"copy_link": "Скопировать ссылку",
"certificate_deadline_required": "Сначала установите срок действия сертификата",
"certificate_deadline_required_description": "Для прохождения курсов по соблюдению требований требуется крайний срок получения сертификата, прежде чем учащиеся смогут быть зачислены. Перед публикацией установите крайний срок в настройках курса.",
"go_to_completion_deadline": "Перейти к сроку завершения",
@@ -295,7 +295,8 @@
"unlocked_all_content": "Tất cả các mục khóa học đã được mở khóa",
"all_content_already_unlocked": "Tất cả nội dung đã được mở khóa",
"content_grouping_enabled": "Đã bật nhóm nội dung",
"content_grouping_disabled": "Đã tắt nhóm nội dung"
"content_grouping_disabled": "Đã tắt nhóm nội dung",
"link_generated": "Liên kết khóa học được cập nhật thành công"
}
},
"people": {
@@ -859,6 +860,9 @@
"description_live": "Các bài học và bài tập bị khóa sẽ được ẩn khỏi học sinh cho đến khi bạn mở khóa chúng — hữu ích nếu bạn phát hành nội dung hàng tuần.",
"unlock_all": "Mở khóa tất cả"
},
"generate_link": "Tạo liên kết mới",
"open_link": "Mở liên kết trong tab mới",
"copy_link": "Sao chép liên kết",
"certificate_deadline_required": "Đặt thời hạn chứng chỉ đầu tiên",
"certificate_deadline_required_description": "Các khóa học tuân thủ yêu cầu thời hạn cấp chứng chỉ trước khi người học có thể đăng ký. Vui lòng đặt thời hạn trong cài đặt khóa học trước khi xuất bản.",
"go_to_completion_deadline": "Đi tới thời hạn hoàn thành",
@@ -27,12 +27,7 @@
<Page.Subtitle>{$t('automation.mcp.page_subtitle')}</Page.Subtitle>
</Page.HeaderContent>
<Page.Action>
<Button
variant="outline"
href="https://classroomio.com/docs/mcp"
target="_blank"
rel="noopener noreferrer"
>
<Button variant="outline" href="https://classroomio.com/docs/mcp" target="_blank" rel="noopener noreferrer">
<ArrowUpRightIcon size={16} />
{$t('automation.mcp.view_docs')}
</Button>