@astratra/saas-kit-ui 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -68,7 +68,7 @@ Utilisez ce paquet si `AstratraDashboardApp` vous convient tel quel ; utilisez `
68
68
 
69
69
  MIT.
70
70
 
71
- ## FeedbackBar : 👍 / 👎 sous une réponse d'IA
71
+ ## FeedbackBar : pouces sous une réponse d’IA
72
72
 
73
73
  Le même composant que dans `@astratra/native-ui`, pour le web. Contrôlé :
74
74
  l'application garde `value` et parle à son serveur (par exemple
@@ -88,7 +88,7 @@ import { FeedbackBar } from '@astratra/saas-kit-ui';
88
88
  Le pouce haut part aussitôt ; le pouce bas ouvre un panneau (raison,
89
89
  commentaire facultatif) ; appuyer sur le pouce choisi le retire. Libellés par
90
90
  défaut en anglais et en français (`locale`), remplaçables un par un (`labels`) ;
91
- raisons ajoutables (`reasons`) ; `icons` remplace les émojis ; `haptics` est une
91
+ raisons ajoutables (`reasons`) ; `icons` remplace les pouces dessinés (contour SVG, plein une fois choisi, jamais d'émojis) ; `haptics` est une
92
92
  fonction facultative (rien par défaut). Accessibilité : groupe nommé, boutons à
93
93
  `aria-pressed`, puces `radio`, panneau `dialog`, Échap le ferme. Le style vient
94
94
  des classes `.astratra-feedback*` de `styles.css` et des variables CSS du kit.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astratra/saas-kit-ui",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Reusable React dashboard shell for Astratra SaaS applications, plus a thumbs up/down feedback bar for AI answers.",
5
5
  "engines": {
6
6
  "node": ">=20"
@@ -9,8 +9,14 @@
9
9
  "main": "src/index.jsx",
10
10
  "module": "src/index.jsx",
11
11
  "exports": {
12
- ".": "./src/index.jsx",
13
- "./api": "./src/lib/api.js",
12
+ ".": {
13
+ "types": "./src/index.d.ts",
14
+ "default": "./src/index.jsx"
15
+ },
16
+ "./api": {
17
+ "types": "./src/lib/api.d.ts",
18
+ "default": "./src/lib/api.js"
19
+ },
14
20
  "./styles.css": "./src/styles/global.css"
15
21
  },
16
22
  "files": [
@@ -35,5 +41,16 @@
35
41
  },
36
42
  "sideEffects": [
37
43
  "**/*.css"
38
- ]
44
+ ],
45
+ "devDependencies": {
46
+ "@types/react": "^19.2.18"
47
+ },
48
+ "types": "src/index.d.ts",
49
+ "typesVersions": {
50
+ "*": {
51
+ "api": [
52
+ "src/lib/api.d.ts"
53
+ ]
54
+ }
55
+ }
39
56
  }
@@ -8,7 +8,24 @@ import {
8
8
  resolveFeedbackLabels
9
9
  } from '../lib/feedbackBar.js';
10
10
 
11
- const THUMBS = { up: '\u{1F44D}', down: '\u{1F44E}' };
11
+ /**
12
+ * The thumbs are drawn, never emoji (an emoji changes with the system and looks out of place in a
13
+ * product). One outline on a 24 grid; down is the same hand turned upside down. Filled once chosen.
14
+ */
15
+ export const THUMB_PATH = 'M7 21H4.5A1.5 1.5 0 0 1 3 19.5v-8A1.5 1.5 0 0 1 4.5 10H7M7 21V10l4.2-6.3A1.6 1.6 0 0 1 14 4.6V9h5a2 2 0 0 1 2 2.4l-1.5 7.9A2 2 0 0 1 17.5 21H7';
16
+
17
+ function thumbIcon(rating, filled) {
18
+ return h('svg', {
19
+ 'aria-hidden': 'true', focusable: 'false', viewBox: '0 0 24 24', width: 18, height: 18,
20
+ className: 'astratra-feedback__icon', fill: 'none', stroke: 'currentColor',
21
+ strokeWidth: 1.8, strokeLinecap: 'round', strokeLinejoin: 'round'
22
+ }, h('path', {
23
+ d: THUMB_PATH,
24
+ fill: filled ? 'currentColor' : 'none',
25
+ fillOpacity: filled ? 0.9 : undefined,
26
+ transform: rating === 'down' ? 'matrix(1 0 0 -1 0 24)' : undefined
27
+ }));
28
+ }
12
29
 
13
30
  /**
14
31
  * Thumbs up / thumbs down under an AI answer. Controlled: the app keeps
@@ -45,7 +62,7 @@ export default function FeedbackBar({
45
62
  const thumb = (rating) => h('button', {
46
63
  key: rating, type: 'button', disabled, 'aria-pressed': value === rating, 'aria-label': words[rating], title: words[rating],
47
64
  className: 'astratra-feedback__thumb', 'data-rating': rating, onClick: () => press(rating)
48
- }, icons?.[rating] ?? h('span', { 'aria-hidden': 'true' }, THUMBS[rating]));
65
+ }, icons?.[rating] ?? thumbIcon(rating, value === rating));
49
66
 
50
67
  const options = feedbackReasonOptions({ reasons, labels: words, selected: reason });
51
68
  return h('div', { id, role: 'group', 'aria-label': words.group, className: ['astratra-feedback', className].filter(Boolean).join(' '), style },
package/src/index.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ import type { CSSProperties, ReactElement, ReactNode } from 'react';
2
+ import type { ApiFetchOptions } from './lib/api';
3
+ export { ApiError, apiFetch } from './lib/api';
4
+ export type { ApiFetchOptions } from './lib/api';
5
+ export default function AstratraDashboardApp(): ReactElement;
6
+ export { AstratraDashboardApp };
7
+ export interface AuthUser { email?: string; role?: string; [key: string]: unknown }
8
+ export interface AuthValue {
9
+ isAuthenticated: boolean;
10
+ token: string | null;
11
+ user: AuthUser | null;
12
+ login(input: { email: string; password: string }): Promise<AuthUser>;
13
+ logout(): void;
14
+ request<T = unknown>(path: string, options?: ApiFetchOptions): Promise<T>;
15
+ }
16
+ export function AuthProvider(props: { children?: ReactNode }): ReactElement;
17
+ export function useAuth(): AuthValue;
18
+ export type DashboardView = 'dashboard' | 'users' | 'settings';
19
+ export function Layout(props: { activeView: DashboardView; onNavigate: (view: DashboardView) => void; children?: ReactNode }): ReactElement;
20
+ export function Notice(props: { tone?: 'neutral' | 'error' | 'success'; children?: ReactNode }): ReactElement | null;
21
+ export function DashboardScreen(): ReactElement;
22
+ export function LoginScreen(props: { onLoggedIn: () => void }): ReactElement;
23
+ export function SettingsScreen(): ReactElement;
24
+ export function UsersScreen(): ReactElement;
25
+ export type FeedbackRating = 'up' | 'down';
26
+ export type FeedbackReason = 'inaccurate' | 'incomplete' | 'off_topic' | 'wrong_source' | 'too_long' | 'unsafe' | 'other';
27
+ export interface FeedbackPayload { rating: FeedbackRating; reason?: string; comment?: string }
28
+ export interface FeedbackLabels {
29
+ group: string; up: string; down: string; sheetTitle: string;
30
+ commentLabel: string; commentPlaceholder: string; submit: string; cancel: string;
31
+ reasons: Readonly<Record<string, string>>;
32
+ counter: (used: number, max: number) => string;
33
+ }
34
+ export type FeedbackLabelOverrides = Partial<FeedbackLabels>;
35
+ export const FEEDBACK_COMMENT_MAX: 500;
36
+ export const FEEDBACK_REASONS: readonly FeedbackReason[];
37
+ export const FEEDBACK_LABELS: Readonly<{ en: Readonly<FeedbackLabels>; fr: Readonly<FeedbackLabels> }>;
38
+ export function resolveFeedbackLabels(options?: { locale?: string; labels?: FeedbackLabelOverrides }): FeedbackLabels;
39
+ export type FeedbackAction = { type: 'none' } | { type: 'remove' } | { type: 'ask' } | { type: 'submit'; payload: { rating: 'up' } };
40
+ export function feedbackPress(value: FeedbackRating | null | undefined, rating: unknown): FeedbackAction;
41
+ export function clampComment(text: unknown, max?: number): string;
42
+ export function buildFeedbackPayload(options: { rating: FeedbackRating; reason?: string | null; comment?: string; reasons?: readonly string[]; maxComment?: number }): FeedbackPayload;
43
+ export function feedbackReasonOptions(options: { reasons?: readonly string[]; labels: FeedbackLabels; selected?: string | null }): { id: string; label: string; selected: boolean }[];
44
+ export interface FeedbackBarProps {
45
+ value?: FeedbackRating | null;
46
+ onSubmit?: (payload: FeedbackPayload) => void;
47
+ onRemove?: () => void;
48
+ disabled?: boolean;
49
+ reasons?: readonly string[];
50
+ maxComment?: number;
51
+ labels?: FeedbackLabelOverrides;
52
+ locale?: string;
53
+ icons?: { up?: ReactNode; down?: ReactNode };
54
+ haptics?: false | (() => void);
55
+ defaultOpen?: boolean;
56
+ className?: string;
57
+ style?: CSSProperties;
58
+ id?: string;
59
+ }
60
+ export function FeedbackBar(props: FeedbackBarProps): ReactElement;
@@ -0,0 +1,15 @@
1
+ export interface ApiFetchOptions {
2
+ apiUrl?: string;
3
+ token?: string | null;
4
+ method?: string;
5
+ body?: unknown;
6
+ fetcher?: typeof fetch;
7
+ onUnauthorized?: () => void;
8
+ }
9
+ export class ApiError extends Error {
10
+ constructor(message: string, options?: { status?: number; data?: unknown; error?: unknown });
11
+ status?: number;
12
+ data?: unknown;
13
+ error?: unknown;
14
+ }
15
+ export function apiFetch<T = unknown>(path: string, options?: ApiFetchOptions): Promise<T>;
@@ -114,10 +114,19 @@ h3 {
114
114
  border-radius: 999px;
115
115
  background: transparent;
116
116
  cursor: pointer;
117
+ color: var(--text, currentColor);
117
118
  opacity: 0.7;
118
119
  transition: opacity 120ms, background 120ms;
119
120
  }
120
121
 
122
+ .astratra-feedback__icon {
123
+ display: block;
124
+ }
125
+
126
+ .astratra-feedback__thumb[aria-pressed="true"] .astratra-feedback__icon {
127
+ color: var(--lime, #d9ff66);
128
+ }
129
+
121
130
  .astratra-feedback__thumb:hover:not(:disabled),
122
131
  .astratra-feedback__thumb:focus-visible {
123
132
  opacity: 1;