@redacto.io/consent-sdk-react 4.0.0 → 5.0.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.
Files changed (160) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/LICENSE +176 -0
  3. package/dist/index.js +231 -96
  4. package/dist/index.mjs +231 -96
  5. package/dist/privacy-center.d.mts +649 -0
  6. package/dist/privacy-center.d.ts +649 -0
  7. package/dist/privacy-center.js +20824 -0
  8. package/dist/privacy-center.mjs +20789 -0
  9. package/package.json +47 -3
  10. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +6 -4
  11. package/src/RedactoNoticeConsent/api/index.ts +2 -0
  12. package/src/RedactoNoticeConsent/api/types.ts +2 -0
  13. package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +128 -62
  14. package/src/RedactoNoticeConsent/language-codes.ts +97 -0
  15. package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +0 -3
  16. package/src/RedactoNoticeConsentInline/api/index.ts +0 -1
  17. package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
  18. package/src/RedactoPrivacyCenter/PrivacyCenterCaseDetailsPage.tsx +154 -0
  19. package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
  20. package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
  21. package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +30 -0
  22. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +173 -0
  23. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +55 -0
  24. package/src/RedactoPrivacyCenter/api/actions.ts +476 -0
  25. package/src/RedactoPrivacyCenter/api/client.ts +524 -0
  26. package/src/RedactoPrivacyCenter/api/config.ts +33 -0
  27. package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
  28. package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
  29. package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
  30. package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
  31. package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
  32. package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
  33. package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
  34. package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
  35. package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
  36. package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
  37. package/src/RedactoPrivacyCenter/components/CaseDetails/AttachmentCard.tsx +38 -0
  38. package/src/RedactoPrivacyCenter/components/CaseDetails/ChatBubble.tsx +31 -0
  39. package/src/RedactoPrivacyCenter/components/CaseDetails/DocumentRequestCard.tsx +73 -0
  40. package/src/RedactoPrivacyCenter/components/CaseDetails/MessageComposer.tsx +87 -0
  41. package/src/RedactoPrivacyCenter/components/CaseDetails/MessageItem.tsx +95 -0
  42. package/src/RedactoPrivacyCenter/components/CaseDetails/MessagesTab.tsx +147 -0
  43. package/src/RedactoPrivacyCenter/components/CaseDetails/RequestDetailsTab.tsx +171 -0
  44. package/src/RedactoPrivacyCenter/components/CaseDetails/StatusEvent.tsx +23 -0
  45. package/src/RedactoPrivacyCenter/components/CaseDetails/useFileUpload.ts +116 -0
  46. package/src/RedactoPrivacyCenter/components/CaseDetails/useMessagePolling.ts +36 -0
  47. package/src/RedactoPrivacyCenter/components/CaseDetails/utils.ts +56 -0
  48. package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +469 -0
  49. package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +426 -0
  50. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +732 -0
  51. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +238 -0
  52. package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
  53. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
  54. package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
  55. package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
  56. package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
  57. package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +304 -0
  58. package/src/RedactoPrivacyCenter/components/Form.tsx +1596 -0
  59. package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +174 -0
  60. package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +337 -0
  61. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +330 -0
  62. package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
  63. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +304 -0
  64. package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +232 -0
  65. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
  66. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +408 -0
  67. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
  68. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
  69. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
  70. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
  71. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
  72. package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
  73. package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +159 -0
  74. package/src/RedactoPrivacyCenter/components/constant.ts +16 -0
  75. package/src/RedactoPrivacyCenter/components/types.ts +76 -0
  76. package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
  77. package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
  78. package/src/RedactoPrivacyCenter/config/table.ts +25 -0
  79. package/src/RedactoPrivacyCenter/context/AuthContext.tsx +137 -0
  80. package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +41 -0
  81. package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
  82. package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +96 -0
  83. package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
  84. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
  85. package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
  86. package/src/RedactoPrivacyCenter/context/types.ts +14 -0
  87. package/src/RedactoPrivacyCenter/lib/api.ts +566 -0
  88. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
  89. package/src/RedactoPrivacyCenter/lib/constants.ts +161 -0
  90. package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
  91. package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
  92. package/src/RedactoPrivacyCenter/lib/queries.ts +134 -0
  93. package/src/RedactoPrivacyCenter/lib/types.ts +531 -0
  94. package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
  95. package/src/RedactoPrivacyCenter/lib/utils.ts +626 -0
  96. package/src/RedactoPrivacyCenter/locales/as/translation.json +280 -0
  97. package/src/RedactoPrivacyCenter/locales/bho/translation.json +280 -0
  98. package/src/RedactoPrivacyCenter/locales/bn/translation.json +280 -0
  99. package/src/RedactoPrivacyCenter/locales/brx/translation.json +280 -0
  100. package/src/RedactoPrivacyCenter/locales/doi/translation.json +280 -0
  101. package/src/RedactoPrivacyCenter/locales/en/translation.json +255 -0
  102. package/src/RedactoPrivacyCenter/locales/gom/translation.json +280 -0
  103. package/src/RedactoPrivacyCenter/locales/gu/translation.json +280 -0
  104. package/src/RedactoPrivacyCenter/locales/hi/translation.json +280 -0
  105. package/src/RedactoPrivacyCenter/locales/kn/translation.json +280 -0
  106. package/src/RedactoPrivacyCenter/locales/ks/translation.json +280 -0
  107. package/src/RedactoPrivacyCenter/locales/mai/translation.json +280 -0
  108. package/src/RedactoPrivacyCenter/locales/ml/translation.json +280 -0
  109. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +280 -0
  110. package/src/RedactoPrivacyCenter/locales/mr/translation.json +280 -0
  111. package/src/RedactoPrivacyCenter/locales/ne/translation.json +280 -0
  112. package/src/RedactoPrivacyCenter/locales/or/translation.json +280 -0
  113. package/src/RedactoPrivacyCenter/locales/pa/translation.json +280 -0
  114. package/src/RedactoPrivacyCenter/locales/sa/translation.json +280 -0
  115. package/src/RedactoPrivacyCenter/locales/sat/translation.json +280 -0
  116. package/src/RedactoPrivacyCenter/locales/sd/translation.json +280 -0
  117. package/src/RedactoPrivacyCenter/locales/ta/translation.json +280 -0
  118. package/src/RedactoPrivacyCenter/locales/te/translation.json +280 -0
  119. package/src/RedactoPrivacyCenter/locales/ur/translation.json +280 -0
  120. package/src/RedactoPrivacyCenter/styles/base.css +117 -0
  121. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1725 -0
  122. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +813 -0
  123. package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
  124. package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
  125. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +367 -0
  126. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +476 -0
  127. package/src/RedactoPrivacyCenter/table/index.ts +2 -0
  128. package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
  129. package/src/RedactoPrivacyCenter/ui/Footer.tsx +75 -0
  130. package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
  131. package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +93 -0
  132. package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
  133. package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
  134. package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
  135. package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
  136. package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
  137. package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +107 -0
  138. package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
  139. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +132 -0
  140. package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +31 -0
  141. package/src/RedactoPrivacyCenter/ui/PCModal.tsx +106 -0
  142. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +173 -0
  143. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +387 -0
  144. package/src/RedactoPrivacyCenter/ui/PCTable.tsx +149 -0
  145. package/src/RedactoPrivacyCenter/ui/PCText.tsx +41 -0
  146. package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
  147. package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
  148. package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
  149. package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
  150. package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +53 -0
  151. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +72 -0
  152. package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
  153. package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
  154. package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
  155. package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
  156. package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
  157. package/src/privacy-center.ts +108 -0
  158. package/tsconfig.json +2 -0
  159. package/tsup.config.ts +16 -1
  160. package/.turbo/turbo-build.log +0 -21
@@ -0,0 +1,149 @@
1
+ import { type ReactNode, type CSSProperties, type HTMLAttributes, type TdHTMLAttributes, type ThHTMLAttributes, createContext } from 'react';
2
+ import { pcStyles } from '../styles/pcStyles';
3
+
4
+ interface TableContextValue {
5
+ variant?: 'modern' | 'minimal';
6
+ }
7
+
8
+ const TableContext = createContext<TableContextValue>({});
9
+
10
+ // Main Table component
11
+ export type PCTableProps = Readonly<
12
+ HTMLAttributes<HTMLTableElement> & {
13
+ children: ReactNode;
14
+ variant?: 'modern' | 'minimal';
15
+ className?: string;
16
+ style?: CSSProperties;
17
+ }
18
+ >;
19
+
20
+ export function PCTable({ children, variant = 'modern', className = '', style = {}, ...props }: PCTableProps) {
21
+ const tableStyle: CSSProperties = {
22
+ ...pcStyles.table,
23
+ ...style,
24
+ };
25
+
26
+ return (
27
+ <TableContext.Provider value={{ variant }}>
28
+ <table className={className} style={tableStyle} {...props}>
29
+ {children}
30
+ </table>
31
+ </TableContext.Provider>
32
+ );
33
+ }
34
+
35
+ // Header component
36
+ export type PCTableHeaderProps = Readonly<
37
+ HTMLAttributes<HTMLTableSectionElement> & {
38
+ children: ReactNode;
39
+ className?: string;
40
+ style?: CSSProperties;
41
+ }
42
+ >;
43
+
44
+ export function PCTableHeader({ children, className = '', style = {}, ...props }: PCTableHeaderProps) {
45
+ const headerStyle: CSSProperties = {
46
+ ...pcStyles.tableHeader,
47
+ ...style,
48
+ };
49
+
50
+ return (
51
+ <thead className={className} style={headerStyle} {...props}>
52
+ {children}
53
+ </thead>
54
+ );
55
+ }
56
+
57
+ // Body component
58
+ export type PCTableBodyProps = Readonly<
59
+ HTMLAttributes<HTMLTableSectionElement> & {
60
+ children: ReactNode;
61
+ className?: string;
62
+ style?: CSSProperties;
63
+ }
64
+ >;
65
+
66
+ export function PCTableBody({ children, className = '', style = {}, ...props }: PCTableBodyProps) {
67
+ const bodyStyle: CSSProperties = {
68
+ ...style,
69
+ };
70
+
71
+ return (
72
+ <tbody className={className} style={bodyStyle} {...props}>
73
+ {children}
74
+ </tbody>
75
+ );
76
+ }
77
+
78
+ // Row component
79
+ export type PCTableRowProps = Readonly<
80
+ HTMLAttributes<HTMLTableRowElement> & {
81
+ children: ReactNode;
82
+ className?: string;
83
+ style?: CSSProperties;
84
+ }
85
+ >;
86
+
87
+ export function PCTableRow({ children, className = '', style = {}, ...props }: PCTableRowProps) {
88
+ const rowStyle: CSSProperties = {
89
+ ...pcStyles.tableRow,
90
+ ...style,
91
+ };
92
+
93
+ return (
94
+ <tr className={`pc-table-row ${className}`} style={rowStyle} {...props}>
95
+ {children}
96
+ </tr>
97
+ );
98
+ }
99
+
100
+ // Header Cell component
101
+ export type PCTableHeadProps = Readonly<
102
+ ThHTMLAttributes<HTMLTableCellElement> & {
103
+ children: ReactNode;
104
+ className?: string;
105
+ style?: CSSProperties;
106
+ }
107
+ >;
108
+
109
+ export function PCTableHead({ children, className = '', style = {}, ...props }: PCTableHeadProps) {
110
+ const headStyle: CSSProperties = {
111
+ ...pcStyles.tableHeaderCell,
112
+ ...style,
113
+ };
114
+
115
+ return (
116
+ <th className={className} style={headStyle} {...props}>
117
+ {children}
118
+ </th>
119
+ );
120
+ }
121
+
122
+ // Cell component
123
+ export type PCTableCellProps = Readonly<
124
+ TdHTMLAttributes<HTMLTableCellElement> & {
125
+ children: ReactNode;
126
+ className?: string;
127
+ style?: CSSProperties;
128
+ }
129
+ >;
130
+
131
+ export function PCTableCell({ children, className = '', style = {}, ...props }: PCTableCellProps) {
132
+ const cellStyle: CSSProperties = {
133
+ ...pcStyles.tableCell,
134
+ ...style,
135
+ };
136
+
137
+ return (
138
+ <td className={className} style={cellStyle} {...props}>
139
+ {children}
140
+ </td>
141
+ );
142
+ }
143
+
144
+ // Compound component pattern
145
+ PCTable.Header = PCTableHeader;
146
+ PCTable.Body = PCTableBody;
147
+ PCTable.Row = PCTableRow;
148
+ PCTable.Head = PCTableHead;
149
+ PCTable.Cell = PCTableCell;
@@ -0,0 +1,41 @@
1
+ import { type ReactNode, type CSSProperties } from 'react';
2
+ import { pcStyles } from '../styles/pcStyles';
3
+
4
+ export type PCTextProps = Readonly<{
5
+ children: ReactNode;
6
+ className?: string;
7
+ style?: CSSProperties;
8
+ as?: 'p' | 'span' | 'div' | 'label';
9
+ size?: 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl';
10
+ weight?: 'normal' | 'medium' | 'semibold' | 'bold';
11
+ color?: 'primary' | 'secondary' | 'tertiary' | 'inverse' | 'disabled';
12
+ align?: 'left' | 'center' | 'right';
13
+ title?: string;
14
+ }>;
15
+
16
+ export function PCText({
17
+ children,
18
+ className = '',
19
+ style = {},
20
+ as: Component = 'p',
21
+ size = 'base',
22
+ weight = 'normal',
23
+ color = 'primary',
24
+ align,
25
+ title,
26
+ }: PCTextProps) {
27
+ const baseStyle: CSSProperties = {
28
+ ...pcStyles.text,
29
+ ...pcStyles[`text${size.charAt(0).toUpperCase() + size.slice(1)}` as keyof typeof pcStyles] as CSSProperties,
30
+ ...pcStyles[`fontWeight${weight.charAt(0).toUpperCase() + weight.slice(1)}` as keyof typeof pcStyles] as CSSProperties,
31
+ ...pcStyles[`text${color.charAt(0).toUpperCase() + color.slice(1)}` as keyof typeof pcStyles] as CSSProperties,
32
+ ...(align && pcStyles[`text${align.charAt(0).toUpperCase() + align.slice(1)}` as keyof typeof pcStyles] as CSSProperties),
33
+ ...style,
34
+ };
35
+
36
+ return (
37
+ <Component className={className} style={baseStyle} title={title}>
38
+ {children}
39
+ </Component>
40
+ );
41
+ }
@@ -0,0 +1,43 @@
1
+ import { type ReactNode, type CSSProperties, type TextareaHTMLAttributes } from 'react';
2
+ import { pcStyles } from '../styles/pcStyles';
3
+
4
+ export interface PCTextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'style' | 'size'> {
5
+ className?: string;
6
+ style?: CSSProperties;
7
+ label?: ReactNode;
8
+ error?: boolean;
9
+ size?: 'sm' | 'md' | 'lg';
10
+ }
11
+
12
+ export function PCTextarea({
13
+ className = '',
14
+ style = {},
15
+ label,
16
+ error = false,
17
+ size = 'md',
18
+ ...props
19
+ }: PCTextareaProps) {
20
+ const textareaStyle: CSSProperties = {
21
+ ...pcStyles.textarea,
22
+ ...(size === 'sm' && pcStyles.inputSm),
23
+ ...(size === 'lg' && pcStyles.inputLg),
24
+ ...(error && pcStyles.inputError),
25
+ ...(props.disabled && pcStyles.inputDisabled),
26
+ ...style,
27
+ };
28
+
29
+ const wrapperStyle: CSSProperties = {
30
+ ...pcStyles.inputWrapper,
31
+ };
32
+
33
+ return (
34
+ <div className={className} style={wrapperStyle}>
35
+ {label && <label style={pcStyles.inputLabel}>{label}</label>}
36
+ <textarea
37
+ className="pc-textarea"
38
+ style={textareaStyle}
39
+ {...props}
40
+ />
41
+ </div>
42
+ );
43
+ }
@@ -0,0 +1,37 @@
1
+ import { type ReactNode, type CSSProperties } from 'react';
2
+ import { pcStyles } from '../styles/pcStyles';
3
+
4
+ export interface PCTitleProps {
5
+ children: ReactNode;
6
+ className?: string;
7
+ style?: CSSProperties;
8
+ as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
9
+ }
10
+
11
+ export function PCTitle({
12
+ children,
13
+ className = '',
14
+ style = {},
15
+ as: Component = 'h3',
16
+ }: PCTitleProps) {
17
+ const sizeMap: Record<string, keyof typeof pcStyles> = {
18
+ h1: 'titleH1',
19
+ h2: 'titleH2',
20
+ h3: 'titleH3',
21
+ h4: 'titleH4',
22
+ h5: 'titleH5',
23
+ h6: 'titleH6',
24
+ };
25
+
26
+ const baseStyle: CSSProperties = {
27
+ ...pcStyles.title,
28
+ ...(pcStyles[sizeMap[Component] as keyof typeof pcStyles] as CSSProperties),
29
+ ...style,
30
+ };
31
+
32
+ return (
33
+ <Component className={className} style={baseStyle}>
34
+ {children}
35
+ </Component>
36
+ );
37
+ }
@@ -0,0 +1,119 @@
1
+ import { type ReactNode, type CSSProperties, useState, useRef, useEffect } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { pcStyles } from '../styles/pcStyles';
4
+
5
+ export interface PCTooltipProps {
6
+ content: ReactNode;
7
+ children: ReactNode;
8
+ placement?: 'top' | 'bottom' | 'left' | 'right';
9
+ className?: string;
10
+ style?: CSSProperties;
11
+ color?: 'default' | 'invert';
12
+ }
13
+
14
+ export function PCTooltip({
15
+ content,
16
+ children,
17
+ placement = 'top',
18
+ className = '',
19
+ style = {},
20
+ color = 'default',
21
+ }: PCTooltipProps) {
22
+ const [isVisible, setIsVisible] = useState(false);
23
+ const [position, setPosition] = useState({ top: 0, left: 0 });
24
+ const triggerRef = useRef<HTMLDivElement>(null);
25
+ const tooltipRef = useRef<HTMLDivElement>(null);
26
+
27
+ const updatePosition = () => {
28
+ if (!triggerRef.current || !tooltipRef.current) return;
29
+
30
+ const triggerRect = triggerRef.current.getBoundingClientRect();
31
+ const tooltipRect = tooltipRef.current.getBoundingClientRect();
32
+ const gap = 2;
33
+ const viewportGutter = 12;
34
+
35
+ let top = 0;
36
+ let left = 0;
37
+
38
+ switch (placement) {
39
+ case 'top':
40
+ top = triggerRect.top - tooltipRect.height - gap;
41
+ left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
42
+ break;
43
+ case 'bottom':
44
+ top = triggerRect.bottom + gap;
45
+ left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
46
+ break;
47
+ case 'left':
48
+ top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
49
+ left = triggerRect.left - tooltipRect.width - gap;
50
+ break;
51
+ case 'right':
52
+ top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
53
+ left = triggerRect.right + gap;
54
+ break;
55
+ }
56
+
57
+ const maxLeft = window.innerWidth - tooltipRect.width - viewportGutter;
58
+ const maxTop = window.innerHeight - tooltipRect.height - viewportGutter;
59
+
60
+ left = Math.min(Math.max(left, viewportGutter), Math.max(viewportGutter, maxLeft));
61
+ top = Math.min(Math.max(top, viewportGutter), Math.max(viewportGutter, maxTop));
62
+
63
+ setPosition({ top, left });
64
+ };
65
+
66
+ useEffect(() => {
67
+ if (isVisible) {
68
+ updatePosition();
69
+ window.addEventListener('scroll', updatePosition, true);
70
+ window.addEventListener('resize', updatePosition);
71
+ return () => {
72
+ window.removeEventListener('scroll', updatePosition, true);
73
+ window.removeEventListener('resize', updatePosition);
74
+ };
75
+ }
76
+ }, [isVisible, placement]);
77
+
78
+ const tooltipStyle: CSSProperties = {
79
+ ...pcStyles.tooltip,
80
+ ...(color === 'invert' && {
81
+ backgroundColor: '#ffffff',
82
+ color: '#1f2937',
83
+ border: '1px solid #e5e7eb',
84
+ boxShadow: '0 8px 18px rgba(15, 23, 42, 0.10)',
85
+ }),
86
+ position: 'fixed',
87
+ top: `${position.top}px`,
88
+ left: `${position.left}px`,
89
+ display: isVisible ? 'block' : 'none',
90
+ padding: '0.375rem 0.625rem',
91
+ fontSize: '0.8125rem',
92
+ lineHeight: 1.2,
93
+ borderRadius: '0.75rem',
94
+ ...style,
95
+ };
96
+
97
+ return (
98
+ <>
99
+ <div
100
+ ref={triggerRef}
101
+ onMouseEnter={() => setIsVisible(true)}
102
+ onMouseLeave={() => setIsVisible(false)}
103
+ style={{ display: 'inline-block', width: '100%' }}
104
+ >
105
+ {children}
106
+ </div>
107
+ {isVisible && typeof document !== 'undefined' && createPortal(
108
+ <div
109
+ ref={tooltipRef}
110
+ className={`pc-tooltip ${className}`}
111
+ style={tooltipStyle}
112
+ >
113
+ {content}
114
+ </div>,
115
+ document.body
116
+ )}
117
+ </>
118
+ );
119
+ }
@@ -0,0 +1,25 @@
1
+ import { pcStyles } from '../styles/pcStyles';
2
+
3
+ type SimpleBarProps = {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ style?: React.CSSProperties;
7
+ };
8
+
9
+ export default function SimpleBar({
10
+ children,
11
+ className,
12
+ style,
13
+ }: SimpleBarProps) {
14
+ return (
15
+ <div
16
+ className={`pc-scrollbar ${className || ''}`}
17
+ style={{
18
+ ...pcStyles.overflowYAuto,
19
+ ...style,
20
+ }}
21
+ >
22
+ {children}
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,53 @@
1
+ import { PCBadge } from './index';
2
+
3
+ type StatusBadgeProps = {
4
+ status?: 'success' | 'warning' | 'error' | 'info' | 'pending' | string;
5
+ children?: React.ReactNode;
6
+ className?: string;
7
+ };
8
+
9
+ const predefinedStatusMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'pending'> = {
10
+ success: 'success',
11
+ warning: 'warning',
12
+ error: 'error',
13
+ info: 'info',
14
+ pending: 'pending',
15
+ active: 'success',
16
+ completed: 'success',
17
+ processing: 'warning',
18
+ in_progress: 'warning',
19
+ rejected: 'error',
20
+ declined: 'error',
21
+ expired: 'warning',
22
+ suspended: 'error',
23
+ revoked: 'error',
24
+ withdrawn: 'error',
25
+ };
26
+
27
+ export default function StatusBadge({
28
+ status = 'info',
29
+ children,
30
+ className,
31
+ }: StatusBadgeProps) {
32
+ // Check if it's a predefined status
33
+ const normalizedStatus = status.toLowerCase();
34
+ const mappedStatus = predefinedStatusMap[normalizedStatus] || status;
35
+
36
+ let badgeVariant: 'success' | 'error' | 'warning' | 'info' | 'secondary' = 'secondary';
37
+
38
+ if (mappedStatus === 'success') {
39
+ badgeVariant = 'success';
40
+ } else if (mappedStatus === 'error') {
41
+ badgeVariant = 'error';
42
+ } else if (mappedStatus === 'warning') {
43
+ badgeVariant = 'warning';
44
+ } else if (mappedStatus === 'info') {
45
+ badgeVariant = 'info';
46
+ }
47
+
48
+ return (
49
+ <PCBadge variant={badgeVariant} className={className}>
50
+ {children || status}
51
+ </PCBadge>
52
+ );
53
+ }
@@ -0,0 +1,72 @@
1
+ import { useRef } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { PCActionIcon } from './index';
4
+ import { IoSunnyOutline, IoMoonOutline } from 'react-icons/io5';
5
+ import { useTheme } from '../context/theme';
6
+ import { PCTooltip } from './PCTooltip';
7
+
8
+ const getVar = (varName: string): string => `var(${varName})`;
9
+
10
+ type ThemeToggleProps = {
11
+ className?: string;
12
+ };
13
+
14
+ export default function ThemeToggle({ className }: ThemeToggleProps) {
15
+ const { t } = useTranslation();
16
+ const { theme, toggleTheme } = useTheme();
17
+ const buttonRef = useRef<HTMLButtonElement>(null);
18
+ const tooltipLabel =
19
+ theme === 'light' ? t('switchToDarkMode') : t('switchToLightMode');
20
+
21
+ const handleClick = () => {
22
+ // Capture click position for animation
23
+ let position: { x: number; y: number };
24
+
25
+ if (buttonRef.current) {
26
+ const rect = buttonRef.current.getBoundingClientRect();
27
+ position = {
28
+ x: rect.left + rect.width / 2,
29
+ y: rect.top + rect.height / 2
30
+ };
31
+ } else {
32
+ // Fallback to center
33
+ position = {
34
+ x: window.innerWidth / 2,
35
+ y: window.innerHeight / 2
36
+ };
37
+ }
38
+
39
+ // Store position globally for ThemeTransitionOverlay to access
40
+ if (typeof window !== 'undefined') {
41
+ (window as any).__pcThemeTransitionPosition = position;
42
+ }
43
+
44
+ toggleTheme();
45
+ };
46
+
47
+ return (
48
+ <PCTooltip content={tooltipLabel} placement="bottom" color="invert">
49
+ <div style={{ display: 'inline-flex' }}>
50
+ <PCActionIcon
51
+ ref={buttonRef}
52
+ variant="default"
53
+ className={className}
54
+ onClick={handleClick}
55
+ aria-label={tooltipLabel}
56
+ style={{
57
+ width: '32px',
58
+ height: '32px',
59
+ color: getVar('--pc-text-primary'),
60
+ transition: 'color 0.3s ease, transform 0.2s ease',
61
+ }}
62
+ >
63
+ {theme === 'light' ? (
64
+ <IoMoonOutline size={18} />
65
+ ) : (
66
+ <IoSunnyOutline size={18} />
67
+ )}
68
+ </PCActionIcon>
69
+ </div>
70
+ </PCTooltip>
71
+ );
72
+ }
@@ -0,0 +1,18 @@
1
+ export { PCText, type PCTextProps } from './PCText';
2
+ export { PCTitle, type PCTitleProps } from './PCTitle';
3
+ export { PCButton, type PCButtonProps } from './PCButton';
4
+ export { PCActionIcon, type PCActionIconProps } from './PCActionIcon';
5
+ export { PCInput, type PCInputProps } from './PCInput';
6
+ export { PCTextarea, type PCTextareaProps } from './PCTextarea';
7
+ export { PCCheckbox, type PCCheckboxProps } from './PCCheckbox';
8
+ export { PCSelect, type PCSelectProps, type SelectOption } from './PCSelect';
9
+ export { PCModal, type PCModalProps } from './PCModal';
10
+ export { PCTable, PCTableHeader, PCTableBody, PCTableRow, PCTableHead, PCTableCell, type PCTableProps, type PCTableHeaderProps, type PCTableBodyProps, type PCTableRowProps, type PCTableHeadProps, type PCTableCellProps } from './PCTable';
11
+ export { PCLoader, type PCLoaderProps } from './PCLoader';
12
+ export { PCBadge, type PCBadgeProps } from './PCBadge';
13
+ export { PCTooltip, type PCTooltipProps } from './PCTooltip';
14
+ export { PCPopover, type PCPopoverProps } from './PCPopover';
15
+ export { PCAccordion, PCAccordionHeader, PCAccordionBody, type PCAccordionProps, type PCAccordionHeaderProps, type PCAccordionBodyProps } from './PCAccordion';
16
+ export { PCCollapse, PCCollapseHeader, PCCollapseBody, type PCCollapseProps, type PCCollapseHeaderProps, type PCCollapseBodyProps } from './PCCollapse';
17
+ export { PCAvatar, type PCAvatarProps } from './PCAvatar';
18
+ export { PCEmpty, type PCEmptyProps } from './PCEmpty';
@@ -0,0 +1,28 @@
1
+ // Simple className utility - replaces clsx and tailwind-merge
2
+ // Accepts strings, objects, arrays, and undefined/null values
3
+ type ClassValue = string | number | boolean | undefined | null | Record<string, boolean> | ClassValue[];
4
+
5
+ export function cn(...inputs: ClassValue[]): string {
6
+ const classes: string[] = [];
7
+
8
+ for (const input of inputs) {
9
+ if (!input) continue;
10
+
11
+ if (typeof input === 'string') {
12
+ classes.push(input);
13
+ } else if (typeof input === 'number') {
14
+ classes.push(String(input));
15
+ } else if (Array.isArray(input)) {
16
+ const inner = cn(...input);
17
+ if (inner) classes.push(inner);
18
+ } else if (typeof input === 'object') {
19
+ for (const key in input) {
20
+ if (input[key]) {
21
+ classes.push(key);
22
+ }
23
+ }
24
+ }
25
+ }
26
+
27
+ return classes.filter(Boolean).join(' ');
28
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Unified date formatting utilities for privacy center app
3
+ */
4
+
5
+ export type FormatDateParams = {
6
+ date: string | Date;
7
+ };
8
+
9
+ /**
10
+ * Format date for display in consent records (with time)
11
+ * Format: "January 15, 2025 at 2:30 PM"
12
+ */
13
+ export const formatDate = ({ date }: FormatDateParams): string => {
14
+ return new Date(date).toLocaleDateString('en-US', {
15
+ year: 'numeric',
16
+ month: 'long',
17
+ day: 'numeric',
18
+ hour: '2-digit',
19
+ minute: '2-digit',
20
+ });
21
+ };
22
+
23
+ /**
24
+ * Format date for display in case history (short format)
25
+ * Format: "15 Jan 2025"
26
+ */
27
+ export const formatDateShort = (dateString: string): string => {
28
+ if (!dateString) return '-';
29
+ return new Date(dateString).toLocaleDateString('en-GB', {
30
+ day: 'numeric',
31
+ month: 'short',
32
+ year: 'numeric'
33
+ });
34
+ };
35
+
36
+ /**
37
+ * Format date time for consent manager (with day/month/year)
38
+ * Format: "15 Jan 2025"
39
+ */
40
+ export const formatDateTime = (timestamp: string): string => {
41
+ const date = new Date(timestamp);
42
+ const options: Intl.DateTimeFormatOptions = {
43
+ day: '2-digit',
44
+ month: 'short',
45
+ year: 'numeric',
46
+ };
47
+ return date.toLocaleString('en-US', options);
48
+ };