fix(dashboard): Save button not showing in certificate settings page (#854)

* Fixed Validation of allowing teachers create compliance courses without setting deadline

* greptile highlightCertificateSettings decraration suggestion

* greptile multiple error bar showing fix suggestion

* organization race condition

* Delete screenshots/Screenshot 2026-07-22 at 11.52.00 AM.png

* deleted unwanted files

* code review fixes

* Changed AttentionHightlight component to work with just one parameter

created fixed Route Enums, Paths and Sections for

* Kebab-case naming fix

* Changes for code comments

* Made changes to add tab to the query param

* Apply suggestion from @greptile-apps[bot]

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: Rotimi Best <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Victor Jack
2026-08-18 13:15:49 +01:00
committed by GitHub
co-authored by greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Rotimi Best
parent 74ab74e364
commit 564b0735f0
3 changed files with 25 additions and 17 deletions
@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/state';
import * as UnderlineTabs from '@cio/ui/custom/underline-tabs'; import * as UnderlineTabs from '@cio/ui/custom/underline-tabs';
import { t } from '$lib/utils/functions/translations'; import { t } from '$lib/utils/functions/translations';
@@ -11,25 +10,16 @@
type Props = { type Props = {
errors: Record<string, string>; errors: Record<string, string>;
activeTab?: string; activeTab?: string;
onActiveTabChange?: (tab: string) => void;
}; };
let { errors }: Props = $props(); let { errors, activeTab = 'design', onActiveTabChange }: Props = $props();
let activeTab = $state('design');
// Initialize activeTab from URL parameter if present
$effect(() => {
const tabParam = page.url.searchParams.get('tab');
if (tabParam && (tabParam === 'design' || tabParam === 'settings')) {
activeTab = tabParam;
}
});
</script> </script>
<UpgradeBanner>{$t('upgrade.certificate')}</UpgradeBanner> <UpgradeBanner>{$t('upgrade.certificate')}</UpgradeBanner>
<main class="px-2 md:-mr-3 md:-ml-3"> <main class="px-2 md:-mr-3 md:-ml-3">
<UnderlineTabs.Root bind:value={activeTab} class="w-full"> <UnderlineTabs.Root value={activeTab} onValueChange={(value) => onActiveTabChange?.(value)} class="w-full">
<UnderlineTabs.List> <UnderlineTabs.List>
<UnderlineTabs.Trigger value="design"> <UnderlineTabs.Trigger value="design">
{$t('course.navItem.certificates.tab_design')} {$t('course.navItem.certificates.tab_design')}
@@ -7,15 +7,16 @@
type Props = { type Props = {
errors: Record<string, string>; errors: Record<string, string>;
activeTab?: string; activeTab?: string;
onActiveTabChange?: (tab: string) => void;
}; };
let { errors, activeTab = $bindable('design') }: Props = $props(); let { errors, activeTab = 'design', onActiveTabChange }: Props = $props();
</script> </script>
<IssueCertificateModal /> <IssueCertificateModal />
<RoleBasedSecurity allowedRoles={[1, 2]}> <RoleBasedSecurity allowedRoles={[1, 2]}>
<CertificateEdit {errors} bind:activeTab /> <CertificateEdit {errors} {activeTab} {onActiveTabChange} />
</RoleBasedSecurity> </RoleBasedSecurity>
<RoleBasedSecurity onlyStudent allowedRoles={[3]}> <RoleBasedSecurity onlyStudent allowedRoles={[3]}>
<StudentCertificate /> <StudentCertificate />
@@ -1,4 +1,7 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/state';
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { CertificatesPage } from '$features/course/pages'; import { CertificatesPage } from '$features/course/pages';
import * as Page from '@cio/ui/base/page'; import * as Page from '@cio/ui/base/page';
import { t } from '$lib/utils/functions/translations'; import { t } from '$lib/utils/functions/translations';
@@ -14,7 +17,12 @@
import { openUpgradeModal } from '$lib/utils/functions/org'; import { openUpgradeModal } from '$lib/utils/functions/org';
let errors = $state<Record<string, string>>({}); let errors = $state<Record<string, string>>({});
let certificateActiveTab = $state('design');
const certificateActiveTab = $derived.by(() => {
const tabInParam = page.url.searchParams.get('tab');
return tabInParam === 'settings' ? 'settings' : 'design';
});
let hasUnsavedChanges = $state(false); let hasUnsavedChanges = $state(false);
let savedCertificateState = $state<string | null>(null); let savedCertificateState = $state<string | null>(null);
let savedCertificateStateCourseId = $state<string | null>(null); let savedCertificateStateCourseId = $state<string | null>(null);
@@ -134,6 +142,15 @@
hasUnsavedChanges = false; hasUnsavedChanges = false;
errors = {}; errors = {};
} }
function handleActiveTabChange(tab: string) {
const url = new URL(page.url);
url.searchParams.set('tab', tab);
function handleActiveTabChange(tab: string) {
const url = new URL(page.url);
url.searchParams.set('tab', tab);
replaceState(url, {});
}
</script> </script>
<svelte:head> <svelte:head>
@@ -160,7 +177,7 @@
</Page.Header> </Page.Header>
<Page.Body> <Page.Body>
{#snippet child()} {#snippet child()}
<CertificatesPage {errors} bind:activeTab={certificateActiveTab} /> <CertificatesPage {errors} activeTab={certificateActiveTab} onActiveTabChange={handleActiveTabChange} />
{/snippet} {/snippet}
</Page.Body> </Page.Body>
{#if canEditCertificates && certificateActiveTab === 'settings'} {#if canEditCertificates && certificateActiveTab === 'settings'}