diff --git a/report-window/consent-problems.tsx b/report-window/consent-problems.tsx new file mode 100644 index 0000000..0b4687a --- /dev/null +++ b/report-window/consent-problems.tsx @@ -0,0 +1,43 @@ +import { EmailTemplate3Config } from './email-template-3'; +import hostSettingsRadio from './host-settings-radio'; + +export default function ConsentProblems({ + settings, + host_id, + setConfig, + pronoun, +}: { + host_id: string; + setConfig: React.Dispatch>; + settings: EmailTemplate3Config['hosts_settings'][string]; + pronoun: 0 | 1 | 2 | 3; +}) { + if (settings.legal_basis_type !== 'consent') { + return ''; + } + const p = pronoun; + return ( +
+ {hostSettingsRadio({ + host_id, + setConfig, + field: 'consent_problems' as const, + value: settings.consent_problems, + options: { + claims_consent_but_sends_before_consent: /* HTML */ `Strona wysłała + ${p == 3 ? 'nasze' : 'moje'} dane do ${host_id} zanim + ${['wyraziłem', 'wyraziłam', 'wyraziłom', 'wyraziliśmy'][p]} na to zgodę.`, + claims_consent_but_there_was_no_easy_refuse: /* HTML */ `${[ + 'Kliknąłem', + 'Kliknęłam', + 'Kliknęłom', + 'Kliknęliśmy', + ][p]} + przycisk od wyrażania zgody, ale w okienku o zgodę nie było natychmiastowo + dostępnego przycisku do niewyrażenia zgody jednym kliknięciem.`, + none: 'Żadne z powyższych.', + }, + })} +
+ ); +} diff --git a/report-window/email-host-settings.tsx b/report-window/email-host-settings.tsx new file mode 100644 index 0000000..f7cba39 --- /dev/null +++ b/report-window/email-host-settings.tsx @@ -0,0 +1,108 @@ +import { EmailTemplate3Config } from './email-template-3'; +import hostSettingsDropdown from './host-setting-dropdown'; +import ConsentProblems from './consent-problems'; +import LegitimateInteresProblems from './legitimate-interest-problems'; +import { hostNeedsQuestions } from './host-needs-questions'; + +export function setHostSetting< + P1 extends keyof EmailTemplate3Config['hosts_settings'], + P2 extends keyof EmailTemplate3Config['hosts_settings'][P1] +>( + setConfig: React.Dispatch>, + [p1, p2]: [P1, P2], + value: EmailTemplate3Config['hosts_settings'][P1][P2] +) { + setConfig((v) => { + console.log(v, { + ...v, + hosts_settings: { + ...v.hosts_settings, + [p1]: { + ...v.hosts_settings[p2], + [p2]: value, + }, + }, + }); + return { + ...v, + hosts_settings: { + ...v.hosts_settings, + [p1]: { + ...v.hosts_settings[p1], + [p2]: value, + }, + }, + }; + }); +} + +export default function emailHostSettings( + config: EmailTemplate3Config, + setConfig: React.Dispatch> +) { + if (config.policy_readable == 'null') { + return ''; + } + const p = config.pronoun; + return ( +
+ {Object.entries(config.hosts_settings).map(([host_id, settings]) => ( +
+
+ {host_id}, {hostNeedsQuestions(settings).toString()} +
+

+ Cele przetwarzania danych przez właściciela domeny {host_id}{' '} + {hostSettingsDropdown({ + host_id, + setConfig, + settings, + field: 'presence', + value: settings.presence, + options: { + not_mentioned: ['nie są nigdzie na stronie opisane'], + mentioned_in_policy: [ + 'są opisane w polityce prywatności', + config.policy_readable !== 'yes', + ], + mentioned_in_popup: ['są opisane w okienku RODO'], + }, + })} +

+ {!['not_mentioned', 'null'].includes(settings.presence) ? ( +

+ Wskazana przez administratora podstawa prawna dla{' '} + tego konkretnego celu{' '} + {hostSettingsDropdown({ + host_id, + setConfig, + settings, + field: 'legal_basis_type' as const, + value: settings.legal_basis_type, + options: { + consent: ['to zgoda.'], + legitimate_interest: ['to uzasadniony interes.'], + not_mentioned: ['nie jest wskazana nigdzie na stronie.'], + }, + })} +

+ ) : ( + '' + )} + {!['not_mentioned', 'null'].includes(settings.legal_basis_type) ? ( +
+ {ConsentProblems({ settings, host_id, pronoun: p, setConfig })} + {LegitimateInteresProblems({ + settings, + host_id, + setConfig, + })} +
+ ) : ( + '' + )} +
+ ))} +
+ ); +} diff --git a/report-window/email-template-3-controls.tsx b/report-window/email-template-3-controls.tsx index e7fd579..9008186 100644 --- a/report-window/email-template-3-controls.tsx +++ b/report-window/email-template-3-controls.tsx @@ -1,39 +1,9 @@ import React from 'react'; import { toBase64 } from '../util'; +import ConsentProblems from './consent-problems'; +import emailHostSettings from './email-host-settings'; import { EmailTemplate3Config } from './email-template-3'; -function setHostSetting< - P1 extends keyof EmailTemplate3Config['hosts_settings'], - P2 extends keyof EmailTemplate3Config['hosts_settings'][P1] ->( - setConfig: React.Dispatch>, - [p1, p2]: [P1, P2], - value: EmailTemplate3Config['hosts_settings'][P1][P2] -) { - setConfig((v) => { - console.log(v, { - ...v, - hosts_settings: { - ...v.hosts_settings, - [p1]: { - ...v.hosts_settings[p2], - [p2]: value, - }, - }, - }); - return { - ...v, - hosts_settings: { - ...v.hosts_settings, - [p1]: { - ...v.hosts_settings[p1], - [p2]: value, - }, - }, - }; - }); -} - export default function EmailTemplate3Controls({ config, setConfig, @@ -92,82 +62,7 @@ export default function EmailTemplate3Controls({ - {config.policy_readable !== 'null' ? ( -
- {Object.entries(config.hosts_settings).map(([id, settings]) => ( -
-
{id}
-

- Cele przetwarzania danych przez właściciela domeny {id}{' '} - -

- {!['not_mentioned', 'null'].includes(settings.presence) ? ( -

- Wskazana przez administratora podstawa prawna dla{' '} - tego konkretnego celu{' '} - -

- ) : ( - '' - )} - {!['not_mentioned', 'null'].includes(settings.legal_basis_type) ? ( -
dodatkowe pytania
- ) : ( - '' - )} -
- ))} -
- ) : ( - '' - )} + {emailHostSettings(config, setConfig)}
- setConfig((v) => ({ - ...v, - popup_accept_all_text: e.target.value, - })), - }} - /> -
- ) : ( - '' - )}
+ setHostSetting( + setConfig, + [host_id, field], + e.target.value as EmailTemplate3Config['hosts_settings'][string][typeof field] + ) + } + > + + {Object.entries(options).map( + ([value, [display, disabled]]: [string, [string, boolean]]) => ( + + ) + )} + + ); +} diff --git a/report-window/host-settings-radio.tsx b/report-window/host-settings-radio.tsx new file mode 100644 index 0000000..75836af --- /dev/null +++ b/report-window/host-settings-radio.tsx @@ -0,0 +1,47 @@ +import { normalizeForClassname } from '../util'; +import { EmailTemplate3Config } from './email-template-3'; + +export default function hostSettingsRadio< + Field extends keyof EmailTemplate3Config['hosts_settings'][string] +>({ + options, + host_id, + field, + setConfig, + value, +}: { + host_id: string; + setConfig: React.Dispatch>; + field: Field; + options: Record, string>; + value: EmailTemplate3Config['hosts_settings'][string][Field]; +}) { + return ( +
+ {Object.entries(options).map(([option_value, display]) => ( +
+ { + setConfig((v) => ({ + ...v, + hosts_settings: { + ...v.hosts_settings, + [host_id]: { + ...v.hosts_settings[host_id], + [field]: e.target.value, + }, + }, + })); + }} + /> + +
+ ))} +
+ ); +} diff --git a/report-window/legitimate-interest-problems.tsx b/report-window/legitimate-interest-problems.tsx new file mode 100644 index 0000000..caa548c --- /dev/null +++ b/report-window/legitimate-interest-problems.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { setHostSetting } from './email-host-settings'; +import { EmailTemplate3Config } from './email-template-3'; +import hostSettingsDropdown from './host-setting-dropdown'; + +export default function LegitimateInteresProblems({ + settings, + host_id, + pronoun, + setConfig, +}: { + host_id: string; + settings: EmailTemplate3Config['hosts_settings'][string]; + setConfig: React.Dispatch>; +}) { + if (settings.legal_basis_type !== 'legitimate_interest') { + return ''; + } + const [description, setDescription] = React.useState('marketing'); + return ( +
+
+ Czy administrator strony opisał szczegółowo, na czym polega uzasadniony interes w + kontekście tego celu? + {hostSettingsDropdown({ + settings, + host_id, + setConfig, + field: 'legitimate_interest_activity_specified' as const, + value: settings.legitimate_interest_activity_specified, + options: { + precise: [ + 'Tak, wskazuje jasno na bieżące działania lub korzyści wynikające z takiego przetwarzania danych.', + ], + vague: ['Wskazuje tylko ogólnie, jak np. „marketing” czy „statystyki”.'], + no: ['Nie. Nie wiadomo, na czym ten uzasadniony interes polega.'], + }, + })} +
+ {settings.legitimate_interest_activity_specified === 'vague' ? ( +
+ Jak administrator opisał to, na czym polega uzasadniony interes w kontekście{' '} + {host_id}?{' '} + setDescription(e.target.value)} + /> + +
+ ) : ( + '' + )} +
+ ); +} diff --git a/util.ts b/util.ts index f763a37..79d7ba6 100644 --- a/util.ts +++ b/util.ts @@ -87,17 +87,13 @@ export function parseToObject(str: unknown): Record { result = JSON.parse(str); } else if (typeof str == 'object') { result = str as Record; - original_string = - (result[Symbol.for('originalString')] as string) || - JSON.stringify(str); + original_string = (result[Symbol.for('originalString')] as string) || JSON.stringify(str); } result[Symbol.for('originalString')] = original_string; return result; } -export function isJSONObject( - str: unknown -): str is Record | string | number { +export function isJSONObject(str: unknown): str is Record | string | number { try { const firstChar = JSON.stringify(parseToObject(str))[0]; return ['{', '['].includes(firstChar); @@ -137,9 +133,10 @@ export function reduceConcat(a: T[], b: T[]): T[] { export function getDate() { const d = new Date(); - return `${d.getFullYear()}-${(d.getMonth() + 1) + return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d + .getDate() .toString() - .padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`; + .padStart(2, '0')}`; } export function toBase64(file: File): Promise { @@ -193,8 +190,7 @@ export function isBase64JSON(s: unknown): s is string { export function flattenObject( obj: unknown, - parser: (to_parse: unknown) => string | Record = (id) => - id.toString(), + parser: (to_parse: unknown) => string | Record = (id) => id.toString(), key = '', ret = [] as [string, string][], parsed = false @@ -224,8 +220,7 @@ export function flattenObject( export function flattenObjectEntries( entries: [string, unknown][], - parser: (to_parse: unknown) => string | Record = (id) => - id.toString() + parser: (to_parse: unknown) => string | Record = (id) => id.toString() ): [string, string][] { return flattenObject(Object.fromEntries(entries), parser); } @@ -236,8 +231,7 @@ export function maskString( max_chars_total: number ): string { const amount_of_chars_to_cut = - str.length - - Math.min(str.length * max_fraction_remaining, max_chars_total); + str.length - Math.min(str.length * max_fraction_remaining, max_chars_total); if (amount_of_chars_to_cut == 0) { return str; } @@ -255,3 +249,7 @@ export function safeDecodeURIComponent(s: string) { return s; } } + +export function normalizeForClassname(string: string) { + return string.replace(/[^a-z0-9]/gi, '-'); +}