@msflib/react-certificate¶
Multi-Tenant Certificate module built on TanStack Query, following the same approach as other @msflib/react-* modules.
Install¶
pnpm add @msflib/core @msflib/react-certificate
Peer dependencies¶
- react >= 18
- @tanstack/react-query >= 5
Wrap your app with CertificateProvider inside your QueryClientProvider.
API Reference¶
CertificateProvider¶
A React context provider that manages certificate configurations, placeholder field names, and user certificates.
import { CertificateProvider } from '@msflib/react-certificate';
<QueryClientProvider client={queryClient}>
<CertificateProvider>{/* your app */}</CertificateProvider>
</QueryClientProvider>;
Props
| Prop | Type | Required | Description |
|---|---|---|---|
children |
React.ReactNode |
Yes | Child component tree. |
options |
object |
No | Configuration options for the provider. See below for details. |
options Configuration¶
| Key | Type | Default | Description |
|---|---|---|---|
isWorkspaceScoped |
boolean |
true |
If true, all certificate data is isolated per workspace. |
requireAuth |
boolean |
false |
If true, data will only be fetched when isAuthenticated is true. |
isAuthenticated |
boolean |
false |
The authentication status of the user (e.g. from useAuth().status). |
In Next.js App Router, CertificateProvider must be in a client component (
"use client").
'use client';
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { CertificateProvider } from '@msflib/react-certificate';
const qc = new QueryClient();
export default function Providers({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={qc}>
<CertificateProvider options={{ isWorkspaceScoped: true }}>{children}</CertificateProvider>
</QueryClientProvider>
);
}
Multi-Tenancy¶
This module supports multi-tenancy using the options.isWorkspaceScoped setting and the useActiveWorkspace hook from @msflib/react-shared. All API calls are automatically scoped to the current workspace if isWorkspaceScoped is true (default).
Configuration¶
'use client';
import { configureApplication } from '@msflib/core';
configureApplication({
baseURL: process.env.NEXT_PUBLIC_API_URL!,
accessTokenKey: 'access_token',
endpoints: {
certificate: {
me: '/certificates/me', // default
adminConfig: '/admin/certificates/config', // default
adminFieldNames: '/admin/certificates/fieldnames', // default
adminFonts: '/admin/certificates/fonts', // default
adminUsers: '/admin/certificates/users', // default
adminPreview: '/admin/certificates/config/preview', // default
},
},
});
API¶
getMe¶
Automatically fetches the current user's certificate on mount (GET /certificates/me).
getConfigs¶
Automatically fetches all certificate configurations on mount (GET /admin/certificates/config).
getConfig¶
Synchronous local lookup by config id from the cached list. Returns undefined if not found.
const config = getConfig(10);
uploadConfig¶
Uploads a new certificate template and configuration (POST /admin/certificates/config/upload).
Expects multipart form data:
const formData = new FormData();
formData.append('certificate_template', file);
formData.append('name', 'My Certificate');
formData.append('placeholders', JSON.stringify([...]));
uploadConfig(formData);
updateConfig¶
Updates an existing certificate configuration (PUT /admin/certificates/config/{config_id}).
Expects multipart form data. Patches the matching config in the cache on success.
const formData = new FormData();
formData.append('name', 'Updated Name');
updateConfig(configId, formData);
deleteConfig¶
Deletes a certificate configuration (DELETE /admin/certificates/config/{config_id}).
Removes the deleted config from the cache on success.
previewCertificate¶
Generates a preview of a certificate template (POST /admin/certificates/config/preview).
Expects multipart form data with certificate_template and placeholders.
generateCertificates¶
Generates certificates for users matching the given filters (POST /admin/certificates/config/{config_id}/generate).
generateCertificates(configId, {
level_ids: [1, 2],
track_ids: [3],
stage_ids: [],
update_if_exist: true,
});
addUsersCertificate¶
Adds or updates certificates for specific users (POST /admin/certificates/config/{config_id}/users).
addUsersCertificate(configId, { user_ids: [1, 2, 3] });
deleteUsersCertificate¶
Deletes certificates for specific users (DELETE /admin/certificates/users).
deleteUsersCertificate([1, 2, 3]);
getUserCertificates¶
Automatically fetches all user certificates on mount (GET /admin/certificates/users).
getFieldNames¶
Automatically fetches available placeholder field names on mount (GET /admin/certificates/fieldnames).
getFonts¶
Automatically fetches available fonts on mount (GET /admin/certificates/fonts).
refetch¶
Manually refetch all queries.
Hook Usage¶
import { useCertificate } from '@msflib/react-certificate';
function CertificatePage() {
const {
certificate,
configs,
userCertificates,
fieldNames,
fonts,
getConfig,
uploadConfig,
updateConfig,
deleteConfig,
previewCertificate,
generateCertificates,
addUsersCertificate,
deleteUsersCertificate,
refetch,
loading,
} = useCertificate();
if (loading.certificate) return <div>Loading...</div>;
return (
<div>
<h1>{certificate?.name}</h1>
<ul>
{configs.map((config) => (
<li key={config.id}>
{config.name}
<button onClick={() => deleteConfig(config.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
Returned Hook Shape¶
const {
certificate,
configs,
getConfig,
uploadConfig,
updateConfig,
deleteConfig,
previewCertificate,
generateCertificates,
userCertificates,
addUsersCertificate,
deleteUsersCertificate,
fieldNames,
fonts,
refetch,
loading,
} = useCertificate();
// Shape:
{
certificate: Certificate | null;
configs: CertificateConfig[];
getConfig: (id: number) => CertificateConfig | undefined;
uploadConfig: (data: FormData, options?: MutateOptions) => Promise<CertificateConfig>;
updateConfig: (configId: number, data: FormData, options?: MutateOptions) => Promise<CertificateConfig>;
deleteConfig: (configId: number, options?: MutateOptions) => Promise<CertificateConfig>;
previewCertificate: (data: FormData, options?: MutateOptions) => Promise<string>;
generateCertificates: (configId: number, data: GenerateCertificatesPayload, options?: MutateOptions) => Promise<Certificate[]>;
userCertificates: Certificate[];
addUsersCertificate: (configId: number, data: AddUsersCertificatePayload, options?: MutateOptions) => Promise<Certificate[]>;
deleteUsersCertificate: (userIds: number[], options?: MutateOptions) => Promise<Certificate[]>;
fieldNames: FieldName[];
fonts: Font[];
refetch: () => void;
loading: {
certificate: boolean;
configs: boolean;
userCertificates: boolean;
fieldNames: boolean;
fonts: boolean;
uploadConfig: boolean;
updateConfig: boolean;
deleteConfig: boolean;
preview: boolean;
generate: boolean;
addUsers: boolean;
deleteUsers: boolean;
};
}
Types¶
export type Certificate = {
id: number;
created_at: string;
updated_at: string;
name: string;
url: string;
owner_id: number;
document_type: string;
};
export type CertificateConfig = {
id: number;
created_at: string;
updated_at: string;
name: string;
description: string;
workspace_id: number;
resolution: number;
template_url: string;
use_fallback: boolean;
placeholders: Placeholder[];
data: Record<string, unknown>;
};
export type FieldName = {
id: number;
field_name: string;
type: 'text' | 'image';
};
export type Font = {
font_family: string;
type: 'system' | string;
url: string;
};
Notes¶
CertificateProvidermust be rendered in a client component in Next.js ("use client").- All queries are cached for 60 seconds by default.
getConfigperforms a local cache lookup — it does not fire a network request.uploadConfigandupdateConfigexpectFormDatawith the template file and a JSON-stringifiedplaceholdersfield.addUsersCertificateanddeleteUsersCertificateinvalidate the user certificates cache on success rather than patching it locally, since the returned list reflects server-side state.