feat: certificates, landing pages, ai tutor, website update, compliance

This commit is contained in:
Rotimi Best
2026-05-18 03:47:22 +01:00
parent c31ad63955
commit ac3a2c14a7
644 changed files with 56050 additions and 5439 deletions
@@ -1,10 +1,12 @@
import { presignApi } from '$features/course/api';
import { presignApi } from '$features/course/api/presign.svelte';
import { BaseApiWithErrors, classroomio } from '$lib/utils/services/api';
import type {
AssetStorageSummary,
AssetTranscriptPayload,
AssetUsage,
AssetUsageGraph,
CreateAssetRequest,
GetAssetTranscriptRequest,
GetAssetUsageRequest,
GetYouTubeMetadataRequest,
ListAssetsRequest,
@@ -37,6 +39,7 @@ import type { AttachAssetRequest } from '../utils/types';
import type { DetachAssetRequest } from '../utils/types';
import { mapZodErrorsToTranslations } from '$lib/utils/validation';
import { snackbar } from '$features/ui/snackbar/store';
import type { StartAssetTranscriptionRequest } from '$features/jobs/utils/types';
export class MediaApi extends BaseApiWithErrors {
assets = $state<OrganizationAsset[]>([]);
@@ -380,6 +383,44 @@ export class MediaApi extends BaseApiWithErrors {
key: asset.storageKey
});
}
/**
* Returns transcript payload or null when none exists yet.
*/
async getAssetTranscript(assetId: string): Promise<AssetTranscriptPayload | null> {
let data: AssetTranscriptPayload | null = null;
await this.execute<GetAssetTranscriptRequest>({
requestFn: () => classroomio.organization.assets[':assetId'].transcript.$get({ param: { assetId } }),
logContext: 'fetching asset transcript',
onSuccess: (response) => {
data = response.data;
},
onError: () => {
snackbar.error('snackbar.media_manager.transcript_fetch_failed');
}
});
return data;
}
/** Enqueue extract-audio + Whisper for an existing upload. */
async generateTranscript(assetId: string): Promise<boolean> {
let ok = false;
await this.execute<StartAssetTranscriptionRequest>({
requestFn: () => classroomio.jobs.media.asset[':assetId'].transcribe.$post({ param: { assetId } }),
logContext: 'starting transcription job',
onSuccess: () => {
ok = true;
snackbar.success('snackbar.media_manager.transcription_started');
},
onError: () => {
snackbar.error('snackbar.media_manager.transcription_start_failed');
}
});
return ok;
}
}
export const mediaApi = new MediaApi();
@@ -52,5 +52,10 @@ export type GetYouTubeMetadataResponse = InferResponseType<GetYouTubeMetadataReq
export type GetYouTubeMetadataSuccess = Extract<GetYouTubeMetadataResponse, { success: true }>;
export type YouTubeMetadata = GetYouTubeMetadataSuccess['data'];
export type GetAssetTranscriptRequest = (typeof classroomio.organization)['assets'][':assetId']['transcript']['$get'];
export type GetAssetTranscriptResponse = InferResponseType<GetAssetTranscriptRequest>;
export type GetAssetTranscriptSuccess = Extract<GetAssetTranscriptResponse, { success: true }>;
export type AssetTranscriptPayload = GetAssetTranscriptSuccess['data'];
export type AssetKindFilter = 'all' | 'video' | 'document' | 'image' | 'audio' | 'other';
export type AssetStatusFilter = 'all' | 'active' | 'archived';