@webiny/app-admin 6.5.0 → 6.5.1-beta.1
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/base/Base/DefaultFieldRenderers.js +4 -0
- package/base/Base/DefaultFieldRenderers.js.map +1 -1
- package/base/Base/FieldRenderers/BirthdayInputRenderer.d.ts +21 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.js +39 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.test.d.ts +1 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.test.js +123 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.test.js.map +1 -0
- package/base/Base/UserMenu/SwitchTenant.d.ts +20 -0
- package/base/Base/UserMenu/SwitchTenant.js +45 -0
- package/base/Base/UserMenu/SwitchTenant.js.map +1 -0
- package/base/Base/UserMenu.js +4 -0
- package/base/Base/UserMenu.js.map +1 -1
- package/domain/Identity.d.ts +8 -0
- package/domain/Identity.js +6 -0
- package/domain/Identity.js.map +1 -1
- package/exports/admin/security.d.ts +4 -1
- package/exports/admin/security.js +4 -1
- package/features/formModel/FieldBuilder.test.js +126 -1
- package/features/formModel/FieldBuilder.test.js.map +1 -1
- package/features/formModel/fieldTypes/DateTimeFieldType.d.ts +65 -4
- package/features/formModel/fieldTypes/DateTimeFieldType.js +70 -8
- package/features/formModel/fieldTypes/DateTimeFieldType.js.map +1 -1
- package/features/formModel/fieldTypes/dateLimits.d.ts +37 -0
- package/features/formModel/fieldTypes/dateLimits.js +64 -0
- package/features/formModel/fieldTypes/dateLimits.js.map +1 -0
- package/features/formModel/fieldTypes/dateLimits.test.d.ts +1 -0
- package/features/formModel/fieldTypes/dateLimits.test.js +60 -0
- package/features/formModel/fieldTypes/dateLimits.test.js.map +1 -0
- package/features/formModel/renderers.d.ts +1 -0
- package/features/formModel/renderers.js +1 -0
- package/features/security/LogIn/LogInError.d.ts +15 -0
- package/features/security/LogIn/LogInError.js +25 -0
- package/features/security/LogIn/LogInError.js.map +1 -0
- package/features/security/LogIn/LogInGateway.d.ts +2 -2
- package/features/security/LogIn/LogInGateway.js +22 -5
- package/features/security/LogIn/LogInGateway.js.map +1 -1
- package/features/security/LogIn/LogInRepository.d.ts +1 -2
- package/features/security/LogIn/LogInRepository.js +6 -2
- package/features/security/LogIn/LogInRepository.js.map +1 -1
- package/features/security/LogIn/LogInUseCase.d.ts +7 -2
- package/features/security/LogIn/LogInUseCase.js +53 -3
- package/features/security/LogIn/LogInUseCase.js.map +1 -1
- package/features/security/LogIn/LogInUseCase.test.d.ts +1 -0
- package/features/security/LogIn/LogInUseCase.test.js +321 -0
- package/features/security/LogIn/LogInUseCase.test.js.map +1 -0
- package/features/security/LogIn/abstractions.d.ts +48 -3
- package/features/security/LogIn/abstractions.js.map +1 -1
- package/features/security/LogIn/createLoginMutation.js +40 -27
- package/features/security/LogIn/createLoginMutation.js.map +1 -1
- package/features/security/LogIn/feature.js +2 -0
- package/features/security/LogIn/feature.js.map +1 -1
- package/features/security/LogIn/index.d.ts +1 -0
- package/features/security/LogIn/index.js +1 -0
- package/features/security/LoginTenantResolver/LoginTenantResolver.d.ts +14 -0
- package/features/security/LoginTenantResolver/LoginTenantResolver.js +52 -0
- package/features/security/LoginTenantResolver/LoginTenantResolver.js.map +1 -0
- package/features/security/LoginTenantResolver/abstractions.d.ts +38 -0
- package/features/security/LoginTenantResolver/abstractions.js +5 -0
- package/features/security/LoginTenantResolver/abstractions.js.map +1 -0
- package/features/security/LoginTenantResolver/feature.d.ts +3 -0
- package/features/security/LoginTenantResolver/feature.js +17 -0
- package/features/security/LoginTenantResolver/feature.js.map +1 -0
- package/features/security/LoginTenantResolver/index.d.ts +2 -0
- package/features/security/LoginTenantResolver/index.js +2 -0
- package/package.json +24 -24
- package/presentation/security/components/LoginNoTenantAccess.d.ts +27 -0
- package/presentation/security/components/LoginNoTenantAccess.js +22 -0
- package/presentation/security/components/LoginNoTenantAccess.js.map +1 -0
- package/presentation/security/components/LoginTenantPicker.d.ts +30 -0
- package/presentation/security/components/LoginTenantPicker.js +32 -0
- package/presentation/security/components/LoginTenantPicker.js.map +1 -0
- package/presentation/security/hooks/useAuthentication.d.ts +2 -2
- package/presentation/security/hooks/useAuthentication.js.map +1 -1
- package/presentation/sortable/SortablePresenter.js +3 -2
- package/presentation/sortable/SortablePresenter.js.map +1 -1
|
@@ -17,6 +17,7 @@ import { RadioButtonsRenderer } from "./FieldRenderers/RadioButtonsRenderer.js";
|
|
|
17
17
|
import { CheckboxesRenderer } from "./FieldRenderers/CheckboxesRenderer.js";
|
|
18
18
|
import { DateTimeRenderer } from "./FieldRenderers/DateTimeRenderer.js";
|
|
19
19
|
import { DateTimeInputsRenderer } from "./FieldRenderers/DateTimeInputsRenderer.js";
|
|
20
|
+
import { BirthdayInputRenderer } from "./FieldRenderers/BirthdayInputRenderer.js";
|
|
20
21
|
import { HiddenRenderer } from "./FieldRenderers/HiddenRenderer.js";
|
|
21
22
|
import { KeyValueTagsRenderer } from "./FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js";
|
|
22
23
|
import { FilePickerRenderer } from "./FieldRenderers/FilePickerRenderer.js";
|
|
@@ -96,6 +97,9 @@ const DefaultFieldRenderers = ()=>/*#__PURE__*/ react.createElement(react.Fragme
|
|
|
96
97
|
}), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
|
|
97
98
|
name: "dateTimeInputs",
|
|
98
99
|
component: DateTimeInputsRenderer
|
|
100
|
+
}), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
|
|
101
|
+
name: "birthdayInput",
|
|
102
|
+
component: BirthdayInputRenderer
|
|
99
103
|
}), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
|
|
100
104
|
name: "hidden",
|
|
101
105
|
component: HiddenRenderer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base/Base/DefaultFieldRenderers.js","sources":["../../../src/base/Base/DefaultFieldRenderers.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig } from \"~/config/AdminConfig.js\";\nimport { InputRenderer } from \"~/base/Base/FieldRenderers/InputRenderer.js\";\nimport { SelectRenderer } from \"~/base/Base/FieldRenderers/SelectRenderer.js\";\nimport { ObjectRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js\";\nimport { ObjectAccordionMultipleRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.js\";\nimport { DynamicZoneRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js\";\nimport { PassthroughRenderer } from \"~/base/Base/FieldRenderers/PassthroughRenderer.js\";\nimport { TextareaRenderer } from \"~/base/Base/FieldRenderers/TextareaRenderer.js\";\nimport { SwitchRenderer } from \"~/base/Base/FieldRenderers/SwitchRenderer.js\";\nimport { NumberInputRenderer } from \"~/base/Base/FieldRenderers/NumberInputRenderer.js\";\nimport { NumberInputsRenderer } from \"~/base/Base/FieldRenderers/NumberInputsRenderer.js\";\nimport { TextInputsRenderer } from \"~/base/Base/FieldRenderers/TextInputsRenderer.js\";\nimport { TextareasRenderer } from \"~/base/Base/FieldRenderers/TextareasRenderer.js\";\nimport { TagsRenderer } from \"~/base/Base/FieldRenderers/TagsRenderer.js\";\nimport { RadioButtonsRenderer } from \"~/base/Base/FieldRenderers/RadioButtonsRenderer.js\";\nimport { CheckboxesRenderer } from \"~/base/Base/FieldRenderers/CheckboxesRenderer.js\";\nimport { DateTimeRenderer } from \"~/base/Base/FieldRenderers/DateTimeRenderer.js\";\nimport { DateTimeInputsRenderer } from \"~/base/Base/FieldRenderers/DateTimeInputsRenderer.js\";\nimport { HiddenRenderer } from \"~/base/Base/FieldRenderers/HiddenRenderer.js\";\nimport { KeyValueTagsRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js\";\nimport { FilePickerRenderer } from \"~/base/Base/FieldRenderers/FilePickerRenderer.js\";\nimport { FileUrlPickerRenderer } from \"~/base/Base/FieldRenderers/FileUrlPickerRenderer.js\";\nimport { MultiFilePickerRenderer } from \"~/base/Base/FieldRenderers/MultiFilePickerRenderer.js\";\nimport { CodeEditorRenderer } from \"~/base/Base/FieldRenderers/CodeEditorRenderer.js\";\nimport { LexicalRenderer } from \"~/base/Base/FieldRenderers/LexicalRenderer.js\";\nimport { LexicalEditorsRenderer } from \"~/base/Base/FieldRenderers/LexicalEditorsRenderer.js\";\nimport { PasswordInputRenderer } from \"~/base/Base/FieldRenderers/PasswordInputRenderer.js\";\nimport { PermissionsRenderer } from \"~/base/Base/FieldRenderers/PermissionsRenderer.js\";\nimport { RolesMultiSelectRenderer } from \"~/base/Base/FieldRenderers/RolesMultiSelectRenderer.js\";\nimport { ApiKeyTokenRenderer } from \"~/base/Base/FieldRenderers/ApiKeyTokenRenderer.js\";\nimport { MultiAutoCompleteRenderer } from \"~/base/Base/FieldRenderers/MultiAutoCompleteRenderer.js\";\nimport { AutoCompleteRenderer } from \"~/base/Base/FieldRenderers/AutoCompleteRenderer.js\";\nimport { MultiSelectRenderer } from \"~/base/Base/FieldRenderers/MultiSelectRenderer.js\";\nimport { RegisterFeature } from \"~/exports/admin.js\";\nimport { MultiValueDynamicZoneFeature } from \"~/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone/feature.js\";\n\nexport const DefaultFieldRenderers = () => {\n return (\n <>\n <RegisterFeature feature={MultiValueDynamicZoneFeature} />\n <AdminConfig>\n <AdminConfig.Form.FieldRenderer name={\"textInput\"} component={InputRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"textarea\"} component={TextareaRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"select\"} component={SelectRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"autoComplete\"}\n component={AutoCompleteRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiSelect\"}\n component={MultiSelectRenderer}\n />\n {/* @deprecated \"dropdown\" is for BC only./ */}\n <AdminConfig.Form.FieldRenderer name={\"dropdown\"} component={SelectRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"objectAccordionSingle\"}\n component={ObjectRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"objectAccordionMultiple\"}\n component={ObjectAccordionMultipleRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dynamicZone\"}\n component={DynamicZoneRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"passthrough\"}\n component={PassthroughRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"switch\"} component={SwitchRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"numberInput\"}\n component={NumberInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"numberInputs\"}\n component={NumberInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"textInputs\"}\n component={TextInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"textareas\"} component={TextareasRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"tags\"} component={TagsRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"radioButtons\"}\n component={RadioButtonsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"checkboxes\"}\n component={CheckboxesRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dateTimeInput\"}\n component={DateTimeRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dateTimeInputs\"}\n component={DateTimeInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"hidden\"} component={HiddenRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"keyValueTags\"}\n component={KeyValueTagsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"filePicker\"}\n component={FilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiFilePicker\"}\n component={MultiFilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"fileUrlPicker\"}\n component={FileUrlPickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"codeEditor\"}\n component={CodeEditorRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"lexical\"} component={LexicalRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"lexicalEditors\"}\n component={LexicalEditorsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"passwordInput\"}\n component={PasswordInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"permissions\"}\n component={PermissionsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"rolesMultiSelect\"}\n component={RolesMultiSelectRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"apiKeyToken\"}\n component={ApiKeyTokenRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiAutoComplete\"}\n component={MultiAutoCompleteRenderer}\n />\n </AdminConfig>\n </>\n );\n};\n"],"names":["DefaultFieldRenderers","RegisterFeature","MultiValueDynamicZoneFeature","AdminConfig","InputRenderer","TextareaRenderer","SelectRenderer","AutoCompleteRenderer","MultiSelectRenderer","ObjectRenderer","ObjectAccordionMultipleRenderer","DynamicZoneRenderer","PassthroughRenderer","SwitchRenderer","NumberInputRenderer","NumberInputsRenderer","TextInputsRenderer","TextareasRenderer","TagsRenderer","RadioButtonsRenderer","CheckboxesRenderer","DateTimeRenderer","DateTimeInputsRenderer","HiddenRenderer","KeyValueTagsRenderer","FilePickerRenderer","MultiFilePickerRenderer","FileUrlPickerRenderer","CodeEditorRenderer","LexicalRenderer","LexicalEditorsRenderer","PasswordInputRenderer","PermissionsRenderer","RolesMultiSelectRenderer","ApiKeyTokenRenderer","MultiAutoCompleteRenderer"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCO,MAAMA,wBAAwB,IAC1B,WAAP,GACI,wDACI,oBAACC,iBAAeA;QAAC,SAASC;sBAC1B,oBAACC,aAAWA,MAAAA,WAAAA,GACR,oBAACA,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAa,WAAWC;sBAC9D,oBAACD,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAY,WAAWE;sBAC7D,oBAACF,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAU,WAAWG;sBAC3D,oBAACH,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWI;sBAEf,oBAACJ,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWK;sBAGf,oBAACL,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAY,WAAWG;sBAC7D,oBAACH,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWM;sBAEf,oBAACN,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWO;sBAEf,oBAACP,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWQ;sBAEf,oBAACR,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWS;sBAEf,oBAACT,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAU,WAAWU;sBAC3D,oBAACV,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWW;sBAEf,oBAACX,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWY;sBAEf,oBAACZ,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWa;sBAEf,oBAACb,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAa,WAAWc;sBAC9D,oBAACd,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAQ,WAAWe;sBACzD,oBAACf,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWgB;sBAEf,oBAAChB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWiB;sBAEf,oBAACjB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWkB;sBAEf,oBAAClB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWmB;sBAEf,oBAACnB,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAU,WAAWoB;sBAC3D,oBAACpB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWqB;sBAEf,oBAACrB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWsB;sBAEf,oBAACtB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWuB;sBAEf,oBAACvB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWwB;sBAEf,oBAACxB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWyB;sBAEf,oBAACzB,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAW,WAAW0B;sBAC5D,oBAAC1B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW2B;sBAEf,oBAAC3B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW4B;sBAEf,oBAAC5B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW6B;sBAEf,oBAAC7B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW8B;sBAEf,oBAAC9B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW+B;sBAEf,oBAAC/B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWgC"}
|
|
1
|
+
{"version":3,"file":"base/Base/DefaultFieldRenderers.js","sources":["../../../src/base/Base/DefaultFieldRenderers.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig } from \"~/config/AdminConfig.js\";\nimport { InputRenderer } from \"~/base/Base/FieldRenderers/InputRenderer.js\";\nimport { SelectRenderer } from \"~/base/Base/FieldRenderers/SelectRenderer.js\";\nimport { ObjectRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js\";\nimport { ObjectAccordionMultipleRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.js\";\nimport { DynamicZoneRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js\";\nimport { PassthroughRenderer } from \"~/base/Base/FieldRenderers/PassthroughRenderer.js\";\nimport { TextareaRenderer } from \"~/base/Base/FieldRenderers/TextareaRenderer.js\";\nimport { SwitchRenderer } from \"~/base/Base/FieldRenderers/SwitchRenderer.js\";\nimport { NumberInputRenderer } from \"~/base/Base/FieldRenderers/NumberInputRenderer.js\";\nimport { NumberInputsRenderer } from \"~/base/Base/FieldRenderers/NumberInputsRenderer.js\";\nimport { TextInputsRenderer } from \"~/base/Base/FieldRenderers/TextInputsRenderer.js\";\nimport { TextareasRenderer } from \"~/base/Base/FieldRenderers/TextareasRenderer.js\";\nimport { TagsRenderer } from \"~/base/Base/FieldRenderers/TagsRenderer.js\";\nimport { RadioButtonsRenderer } from \"~/base/Base/FieldRenderers/RadioButtonsRenderer.js\";\nimport { CheckboxesRenderer } from \"~/base/Base/FieldRenderers/CheckboxesRenderer.js\";\nimport { DateTimeRenderer } from \"~/base/Base/FieldRenderers/DateTimeRenderer.js\";\nimport { DateTimeInputsRenderer } from \"~/base/Base/FieldRenderers/DateTimeInputsRenderer.js\";\nimport { BirthdayInputRenderer } from \"~/base/Base/FieldRenderers/BirthdayInputRenderer.js\";\nimport { HiddenRenderer } from \"~/base/Base/FieldRenderers/HiddenRenderer.js\";\nimport { KeyValueTagsRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js\";\nimport { FilePickerRenderer } from \"~/base/Base/FieldRenderers/FilePickerRenderer.js\";\nimport { FileUrlPickerRenderer } from \"~/base/Base/FieldRenderers/FileUrlPickerRenderer.js\";\nimport { MultiFilePickerRenderer } from \"~/base/Base/FieldRenderers/MultiFilePickerRenderer.js\";\nimport { CodeEditorRenderer } from \"~/base/Base/FieldRenderers/CodeEditorRenderer.js\";\nimport { LexicalRenderer } from \"~/base/Base/FieldRenderers/LexicalRenderer.js\";\nimport { LexicalEditorsRenderer } from \"~/base/Base/FieldRenderers/LexicalEditorsRenderer.js\";\nimport { PasswordInputRenderer } from \"~/base/Base/FieldRenderers/PasswordInputRenderer.js\";\nimport { PermissionsRenderer } from \"~/base/Base/FieldRenderers/PermissionsRenderer.js\";\nimport { RolesMultiSelectRenderer } from \"~/base/Base/FieldRenderers/RolesMultiSelectRenderer.js\";\nimport { ApiKeyTokenRenderer } from \"~/base/Base/FieldRenderers/ApiKeyTokenRenderer.js\";\nimport { MultiAutoCompleteRenderer } from \"~/base/Base/FieldRenderers/MultiAutoCompleteRenderer.js\";\nimport { AutoCompleteRenderer } from \"~/base/Base/FieldRenderers/AutoCompleteRenderer.js\";\nimport { MultiSelectRenderer } from \"~/base/Base/FieldRenderers/MultiSelectRenderer.js\";\nimport { RegisterFeature } from \"~/exports/admin.js\";\nimport { MultiValueDynamicZoneFeature } from \"~/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone/feature.js\";\n\nexport const DefaultFieldRenderers = () => {\n return (\n <>\n <RegisterFeature feature={MultiValueDynamicZoneFeature} />\n <AdminConfig>\n <AdminConfig.Form.FieldRenderer name={\"textInput\"} component={InputRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"textarea\"} component={TextareaRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"select\"} component={SelectRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"autoComplete\"}\n component={AutoCompleteRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiSelect\"}\n component={MultiSelectRenderer}\n />\n {/* @deprecated \"dropdown\" is for BC only./ */}\n <AdminConfig.Form.FieldRenderer name={\"dropdown\"} component={SelectRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"objectAccordionSingle\"}\n component={ObjectRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"objectAccordionMultiple\"}\n component={ObjectAccordionMultipleRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dynamicZone\"}\n component={DynamicZoneRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"passthrough\"}\n component={PassthroughRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"switch\"} component={SwitchRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"numberInput\"}\n component={NumberInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"numberInputs\"}\n component={NumberInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"textInputs\"}\n component={TextInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"textareas\"} component={TextareasRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"tags\"} component={TagsRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"radioButtons\"}\n component={RadioButtonsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"checkboxes\"}\n component={CheckboxesRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dateTimeInput\"}\n component={DateTimeRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dateTimeInputs\"}\n component={DateTimeInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"birthdayInput\"}\n component={BirthdayInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"hidden\"} component={HiddenRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"keyValueTags\"}\n component={KeyValueTagsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"filePicker\"}\n component={FilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiFilePicker\"}\n component={MultiFilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"fileUrlPicker\"}\n component={FileUrlPickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"codeEditor\"}\n component={CodeEditorRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"lexical\"} component={LexicalRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"lexicalEditors\"}\n component={LexicalEditorsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"passwordInput\"}\n component={PasswordInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"permissions\"}\n component={PermissionsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"rolesMultiSelect\"}\n component={RolesMultiSelectRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"apiKeyToken\"}\n component={ApiKeyTokenRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiAutoComplete\"}\n component={MultiAutoCompleteRenderer}\n />\n </AdminConfig>\n </>\n );\n};\n"],"names":["DefaultFieldRenderers","RegisterFeature","MultiValueDynamicZoneFeature","AdminConfig","InputRenderer","TextareaRenderer","SelectRenderer","AutoCompleteRenderer","MultiSelectRenderer","ObjectRenderer","ObjectAccordionMultipleRenderer","DynamicZoneRenderer","PassthroughRenderer","SwitchRenderer","NumberInputRenderer","NumberInputsRenderer","TextInputsRenderer","TextareasRenderer","TagsRenderer","RadioButtonsRenderer","CheckboxesRenderer","DateTimeRenderer","DateTimeInputsRenderer","BirthdayInputRenderer","HiddenRenderer","KeyValueTagsRenderer","FilePickerRenderer","MultiFilePickerRenderer","FileUrlPickerRenderer","CodeEditorRenderer","LexicalRenderer","LexicalEditorsRenderer","PasswordInputRenderer","PermissionsRenderer","RolesMultiSelectRenderer","ApiKeyTokenRenderer","MultiAutoCompleteRenderer"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
declare module "../../../features/formModel/abstractions.js" {
|
|
3
|
+
interface IFieldRendererRegistry {
|
|
4
|
+
/**
|
|
5
|
+
* Picks a day, then a month, then a year. It offers only the dates the field's
|
|
6
|
+
* `dateGte`/`dateLte` limits allow; without limits, years run from this year back
|
|
7
|
+
* 120 years.
|
|
8
|
+
*/
|
|
9
|
+
birthdayInput: {
|
|
10
|
+
fieldType: "datetime";
|
|
11
|
+
settings?: undefined;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Renders a date-only field as a step-by-step picker: day, then month, then year.
|
|
17
|
+
* The value is the same `YYYY-MM-DD` string the calendar picker stores.
|
|
18
|
+
*/
|
|
19
|
+
export declare const BirthdayInputRenderer: React.ComponentType<{
|
|
20
|
+
field: import("../../..").IFieldVM;
|
|
21
|
+
}>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import react, { useMemo } from "react";
|
|
2
|
+
import { DatePicker } from "@webiny/admin-ui";
|
|
3
|
+
import { createFieldRenderer } from "../../../features/formModel/createFieldRenderer.js";
|
|
4
|
+
import { getDateLimits, resolveDateLimits } from "../../../features/formModel/fieldTypes/dateLimits.js";
|
|
5
|
+
const toLocalDate = (value)=>{
|
|
6
|
+
const match = value?.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
7
|
+
return match ? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) : void 0;
|
|
8
|
+
};
|
|
9
|
+
const BirthdayInputRenderer = createFieldRenderer(({ field })=>{
|
|
10
|
+
const { min, max } = resolveDateLimits(getDateLimits(field.rendererSettings));
|
|
11
|
+
const minDate = useMemo(()=>toLocalDate(min), [
|
|
12
|
+
min
|
|
13
|
+
]);
|
|
14
|
+
const maxDate = useMemo(()=>toLocalDate(max), [
|
|
15
|
+
max
|
|
16
|
+
]);
|
|
17
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
18
|
+
className: "w-full"
|
|
19
|
+
}, /*#__PURE__*/ react.createElement(DatePicker, {
|
|
20
|
+
hint: field.help,
|
|
21
|
+
type: "birthday",
|
|
22
|
+
value: field.value ?? void 0,
|
|
23
|
+
onChange: (value)=>{
|
|
24
|
+
field.onChange(value);
|
|
25
|
+
field.onBlur();
|
|
26
|
+
},
|
|
27
|
+
disabled: field.disabled,
|
|
28
|
+
minDate: minDate,
|
|
29
|
+
maxDate: maxDate,
|
|
30
|
+
label: field.label,
|
|
31
|
+
description: field.description,
|
|
32
|
+
note: field.note,
|
|
33
|
+
required: field.required,
|
|
34
|
+
validation: field.validation
|
|
35
|
+
}));
|
|
36
|
+
});
|
|
37
|
+
export { BirthdayInputRenderer };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=BirthdayInputRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base/Base/FieldRenderers/BirthdayInputRenderer.js","sources":["../../../../src/base/Base/FieldRenderers/BirthdayInputRenderer.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { DatePicker } from \"@webiny/admin-ui\";\nimport { createFieldRenderer } from \"~/features/formModel/createFieldRenderer.js\";\nimport { getDateLimits, resolveDateLimits } from \"~/features/formModel/fieldTypes/dateLimits.js\";\n\ndeclare module \"../../../features/formModel/abstractions.js\" {\n interface IFieldRendererRegistry {\n /**\n * Picks a day, then a month, then a year. It offers only the dates the field's\n * `dateGte`/`dateLte` limits allow; without limits, years run from this year back\n * 120 years.\n */\n birthdayInput: {\n fieldType: \"datetime\";\n settings?: undefined;\n };\n }\n}\n\n/** A `YYYY-MM-DD` date as local midnight, the way the picker compares dates. */\nconst toLocalDate = (value: string | undefined): Date | undefined => {\n const match = value?.match(/^(\\d{4})-(\\d{2})-(\\d{2})/);\n return match ? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) : undefined;\n};\n\n/**\n * Renders a date-only field as a step-by-step picker: day, then month, then year.\n * The value is the same `YYYY-MM-DD` string the calendar picker stores.\n */\nexport const BirthdayInputRenderer = createFieldRenderer<\"birthdayInput\">(({ field }) => {\n // Worked out on every render, so the limits follow the editor's local date.\n const { min, max } = resolveDateLimits(getDateLimits(field.rendererSettings));\n const minDate = useMemo(() => toLocalDate(min), [min]);\n const maxDate = useMemo(() => toLocalDate(max), [max]);\n\n return (\n <div className=\"w-full\">\n <DatePicker\n hint={field.help}\n type={\"birthday\"}\n value={(field.value as string | undefined) ?? undefined}\n onChange={value => {\n field.onChange(value);\n field.onBlur();\n }}\n disabled={field.disabled}\n minDate={minDate}\n maxDate={maxDate}\n label={field.label}\n description={field.description}\n note={field.note}\n required={field.required}\n validation={field.validation}\n />\n </div>\n );\n});\n"],"names":["toLocalDate","value","match","Date","Number","undefined","BirthdayInputRenderer","createFieldRenderer","field","min","max","resolveDateLimits","getDateLimits","minDate","useMemo","maxDate","DatePicker"],"mappings":";;;;AAoBA,MAAMA,cAAc,CAACC;IACjB,MAAMC,QAAQD,OAAO,MAAM;IAC3B,OAAOC,QAAQ,IAAIC,KAAKC,OAAOF,KAAK,CAAC,EAAE,GAAGE,OAAOF,KAAK,CAAC,EAAE,IAAI,GAAGE,OAAOF,KAAK,CAAC,EAAE,KAAKG;AACxF;AAMO,MAAMC,wBAAwBC,oBAAqC,CAAC,EAAEC,KAAK,EAAE;IAEhF,MAAM,EAAEC,GAAG,EAAEC,GAAG,EAAE,GAAGC,kBAAkBC,cAAcJ,MAAM,gBAAgB;IAC3E,MAAMK,UAAUC,QAAQ,IAAMd,YAAYS,MAAM;QAACA;KAAI;IACrD,MAAMM,UAAUD,QAAQ,IAAMd,YAAYU,MAAM;QAACA;KAAI;IAErD,OAAO,WAAP,GACI,oBAAC;QAAI,WAAU;qBACX,oBAACM,YAAUA;QACP,MAAMR,MAAM,IAAI;QAChB,MAAM;QACN,OAAQA,MAAM,KAAK,IAA2BH;QAC9C,UAAUJ,CAAAA;YACNO,MAAM,QAAQ,CAACP;YACfO,MAAM,MAAM;QAChB;QACA,UAAUA,MAAM,QAAQ;QACxB,SAASK;QACT,SAASE;QACT,OAAOP,MAAM,KAAK;QAClB,aAAaA,MAAM,WAAW;QAC9B,MAAMA,MAAM,IAAI;QAChB,UAAUA,MAAM,QAAQ;QACxB,YAAYA,MAAM,UAAU;;AAI5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import react from "react";
|
|
3
|
+
import { cleanup, fireEvent, render, screen, within } from "@testing-library/react";
|
|
4
|
+
import { AdminUiProvider } from "@webiny/admin-ui";
|
|
5
|
+
import { DateTimeFieldBuilder } from "../../../features/formModel/fieldTypes/DateTimeFieldType.js";
|
|
6
|
+
import { BirthdayInputRenderer } from "./BirthdayInputRenderer.js";
|
|
7
|
+
vi.mock("@webiny/icons/calendar_month.svg", ()=>({
|
|
8
|
+
ReactComponent: ()=>null
|
|
9
|
+
}));
|
|
10
|
+
vi.mock("@webiny/icons/close.svg", ()=>({
|
|
11
|
+
ReactComponent: ()=>null
|
|
12
|
+
}));
|
|
13
|
+
const createAdultField = ()=>{
|
|
14
|
+
const config = new DateTimeFieldBuilder().dateOnly().dateLte({
|
|
15
|
+
expression: "today-18y"
|
|
16
|
+
}).renderer("birthdayInput").build("dob");
|
|
17
|
+
return {
|
|
18
|
+
name: "dob",
|
|
19
|
+
qualifiedName: "dob",
|
|
20
|
+
type: "datetime",
|
|
21
|
+
label: "Date of birth",
|
|
22
|
+
value: void 0,
|
|
23
|
+
validation: {
|
|
24
|
+
isValid: null
|
|
25
|
+
},
|
|
26
|
+
validating: false,
|
|
27
|
+
required: false,
|
|
28
|
+
visible: true,
|
|
29
|
+
disabled: false,
|
|
30
|
+
renderer: config.renderer,
|
|
31
|
+
rendererSettings: config.rendererSettings,
|
|
32
|
+
onChange: vi.fn(),
|
|
33
|
+
onBlur: vi.fn(),
|
|
34
|
+
addItem: vi.fn(),
|
|
35
|
+
removeItem: vi.fn(),
|
|
36
|
+
focusRequested: false,
|
|
37
|
+
clearFocusRequest: vi.fn(),
|
|
38
|
+
context: {}
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const cell = (step, name)=>{
|
|
42
|
+
const panel = document.querySelector(`div[data-step="${step}"]`);
|
|
43
|
+
return within(panel).getByRole("button", {
|
|
44
|
+
name,
|
|
45
|
+
hidden: true
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const years = ()=>{
|
|
49
|
+
const panel = document.querySelector('div[data-step="year"]');
|
|
50
|
+
return within(panel).getAllByRole("button", {
|
|
51
|
+
hidden: true
|
|
52
|
+
}).map((button)=>Number(button.textContent));
|
|
53
|
+
};
|
|
54
|
+
const renderField = (field)=>render(/*#__PURE__*/ react.createElement(AdminUiProvider, null, /*#__PURE__*/ react.createElement(BirthdayInputRenderer, {
|
|
55
|
+
field: field
|
|
56
|
+
})));
|
|
57
|
+
const openPicker = ()=>{
|
|
58
|
+
fireEvent.click(screen.getByRole("button", {
|
|
59
|
+
expanded: false
|
|
60
|
+
}));
|
|
61
|
+
};
|
|
62
|
+
describe("BirthdayInputRenderer with a relative limit", ()=>{
|
|
63
|
+
beforeEach(()=>{
|
|
64
|
+
vi.useFakeTimers({
|
|
65
|
+
toFake: [
|
|
66
|
+
"Date"
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
vi.stubGlobal("ResizeObserver", class {
|
|
70
|
+
observe() {}
|
|
71
|
+
unobserve() {}
|
|
72
|
+
disconnect() {}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
afterEach(()=>{
|
|
76
|
+
cleanup();
|
|
77
|
+
vi.useRealTimers();
|
|
78
|
+
vi.unstubAllGlobals();
|
|
79
|
+
});
|
|
80
|
+
it("starts the year list at the cut-off year", ()=>{
|
|
81
|
+
vi.setSystemTime(new Date(2026, 9, 19, 12));
|
|
82
|
+
renderField(createAdultField());
|
|
83
|
+
openPicker();
|
|
84
|
+
expect(years()[0]).toBe(2008);
|
|
85
|
+
expect(years().some((year)=>year > 2008)).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
it("dims the cut-off year for a day after the cut-off", ()=>{
|
|
88
|
+
vi.setSystemTime(new Date(2026, 9, 19, 12));
|
|
89
|
+
renderField(createAdultField());
|
|
90
|
+
openPicker();
|
|
91
|
+
fireEvent.click(cell("day", "20"));
|
|
92
|
+
fireEvent.click(cell("month", "October"));
|
|
93
|
+
expect(cell("year", "2008").disabled).toBe(true);
|
|
94
|
+
expect(cell("year", "2007").disabled).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
it("allows the cut-off day itself", ()=>{
|
|
97
|
+
vi.setSystemTime(new Date(2026, 9, 19, 12));
|
|
98
|
+
const field = createAdultField();
|
|
99
|
+
renderField(field);
|
|
100
|
+
openPicker();
|
|
101
|
+
fireEvent.click(cell("day", "19"));
|
|
102
|
+
fireEvent.click(cell("month", "October"));
|
|
103
|
+
expect(cell("year", "2008").disabled).toBe(false);
|
|
104
|
+
fireEvent.click(cell("year", "2008"));
|
|
105
|
+
expect(field.onChange).toHaveBeenLastCalledWith("2008-10-19");
|
|
106
|
+
});
|
|
107
|
+
it("moves the cut-off with the date", ()=>{
|
|
108
|
+
vi.setSystemTime(new Date(2026, 9, 20, 12));
|
|
109
|
+
renderField(createAdultField());
|
|
110
|
+
openPicker();
|
|
111
|
+
fireEvent.click(cell("day", "20"));
|
|
112
|
+
fireEvent.click(cell("month", "October"));
|
|
113
|
+
expect(cell("year", "2008").disabled).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
it("moves the year list on 1 January", ()=>{
|
|
116
|
+
vi.setSystemTime(new Date(2027, 0, 1, 12));
|
|
117
|
+
renderField(createAdultField());
|
|
118
|
+
openPicker();
|
|
119
|
+
expect(years()[0]).toBe(2009);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=BirthdayInputRenderer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base/Base/FieldRenderers/BirthdayInputRenderer.test.js","sources":["../../../../src/base/Base/FieldRenderers/BirthdayInputRenderer.test.tsx"],"sourcesContent":["import { afterEach, beforeEach, describe, it, expect, vi } from \"vitest\";\nimport React from \"react\";\nimport { cleanup, fireEvent, render, screen, within } from \"@testing-library/react\";\nimport { AdminUiProvider } from \"@webiny/admin-ui\";\nimport type { IFieldVM } from \"~/features/formModel/abstractions.js\";\nimport { DateTimeFieldBuilder } from \"~/features/formModel/fieldTypes/DateTimeFieldType.js\";\nimport { BirthdayInputRenderer } from \"./BirthdayInputRenderer.js\";\n\n// Tests don't turn SVG files into components, so the picker's icons are stubbed.\nvi.mock(\"@webiny/icons/calendar_month.svg\", () => ({ ReactComponent: () => null }));\nvi.mock(\"@webiny/icons/close.svg\", () => ({ ReactComponent: () => null }));\n\n/** A date-only field of 18 or older, as `datetime()` hands it to the renderer. */\nconst createAdultField = (): IFieldVM => {\n const config = new DateTimeFieldBuilder()\n .dateOnly()\n .dateLte({ expression: \"today-18y\" })\n .renderer(\"birthdayInput\")\n .build(\"dob\");\n\n return {\n name: \"dob\",\n qualifiedName: \"dob\",\n type: \"datetime\",\n label: \"Date of birth\",\n value: undefined,\n validation: { isValid: null },\n validating: false,\n required: false,\n visible: true,\n disabled: false,\n renderer: config.renderer,\n rendererSettings: config.rendererSettings,\n onChange: vi.fn(),\n onBlur: vi.fn(),\n addItem: vi.fn(),\n removeItem: vi.fn(),\n focusRequested: false,\n clearFocusRequest: vi.fn(),\n context: {}\n };\n};\n\n/**\n * A step's grid. The header segments carry `data-step` too, so only the grid `div` matches.\n * Only the current step's grid is visible, so the cells are looked up as hidden as well.\n */\nconst cell = (step: \"day\" | \"month\" | \"year\", name: string) => {\n const panel = document.querySelector<HTMLElement>(`div[data-step=\"${step}\"]`)!;\n return within(panel).getByRole<HTMLButtonElement>(\"button\", { name, hidden: true });\n};\n\nconst years = () => {\n const panel = document.querySelector<HTMLElement>(`div[data-step=\"year\"]`)!;\n return within(panel)\n .getAllByRole(\"button\", { hidden: true })\n .map(button => Number(button.textContent));\n};\n\nconst renderField = (field: IFieldVM) =>\n render(\n <AdminUiProvider>\n <BirthdayInputRenderer field={field} />\n </AdminUiProvider>\n );\n\nconst openPicker = () => {\n fireEvent.click(screen.getByRole(\"button\", { expanded: false }));\n};\n\n/**\n * The renderer works the limits out from the editor's local date on every render. Only `Date`\n * is frozen, so the picker's own timers and animation frames run as usual.\n */\ndescribe(\"BirthdayInputRenderer with a relative limit\", () => {\n beforeEach(() => {\n vi.useFakeTimers({ toFake: [\"Date\"] });\n // Polyfilled per test: jsdom has no ResizeObserver, which the popover and scroll area use.\n vi.stubGlobal(\n \"ResizeObserver\",\n class {\n observe() {}\n unobserve() {}\n disconnect() {}\n }\n );\n });\n\n afterEach(() => {\n cleanup();\n vi.useRealTimers();\n vi.unstubAllGlobals();\n });\n\n it(\"starts the year list at the cut-off year\", () => {\n vi.setSystemTime(new Date(2026, 9, 19, 12));\n renderField(createAdultField());\n openPicker();\n\n expect(years()[0]).toBe(2008);\n expect(years().some(year => year > 2008)).toBe(false);\n });\n\n it(\"dims the cut-off year for a day after the cut-off\", () => {\n vi.setSystemTime(new Date(2026, 9, 19, 12));\n renderField(createAdultField());\n openPicker();\n\n fireEvent.click(cell(\"day\", \"20\"));\n fireEvent.click(cell(\"month\", \"October\"));\n\n expect(cell(\"year\", \"2008\").disabled).toBe(true);\n expect(cell(\"year\", \"2007\").disabled).toBe(false);\n });\n\n it(\"allows the cut-off day itself\", () => {\n vi.setSystemTime(new Date(2026, 9, 19, 12));\n const field = createAdultField();\n renderField(field);\n openPicker();\n\n fireEvent.click(cell(\"day\", \"19\"));\n fireEvent.click(cell(\"month\", \"October\"));\n expect(cell(\"year\", \"2008\").disabled).toBe(false);\n\n fireEvent.click(cell(\"year\", \"2008\"));\n expect(field.onChange).toHaveBeenLastCalledWith(\"2008-10-19\");\n });\n\n it(\"moves the cut-off with the date\", () => {\n vi.setSystemTime(new Date(2026, 9, 20, 12));\n renderField(createAdultField());\n openPicker();\n\n fireEvent.click(cell(\"day\", \"20\"));\n fireEvent.click(cell(\"month\", \"October\"));\n\n expect(cell(\"year\", \"2008\").disabled).toBe(false);\n });\n\n it(\"moves the year list on 1 January\", () => {\n vi.setSystemTime(new Date(2027, 0, 1, 12));\n renderField(createAdultField());\n openPicker();\n\n expect(years()[0]).toBe(2009);\n });\n});\n"],"names":["vi","createAdultField","config","DateTimeFieldBuilder","undefined","cell","step","name","panel","document","within","years","button","Number","renderField","field","render","AdminUiProvider","BirthdayInputRenderer","openPicker","fireEvent","screen","describe","beforeEach","afterEach","cleanup","it","Date","expect","year"],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export declare const SwitchTenant: (() => React.JSX.Element | null) & {
|
|
3
|
+
original: () => React.JSX.Element | null;
|
|
4
|
+
originalName: string;
|
|
5
|
+
displayName: string;
|
|
6
|
+
} & {
|
|
7
|
+
original: (() => React.JSX.Element | null) & {
|
|
8
|
+
original: () => React.JSX.Element | null;
|
|
9
|
+
originalName: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
};
|
|
12
|
+
originalName: string;
|
|
13
|
+
displayName: string;
|
|
14
|
+
} & {
|
|
15
|
+
createDecorator: (decorator: import("@webiny/app").ComponentDecorator<(() => React.JSX.Element | null) & {
|
|
16
|
+
original: () => React.JSX.Element | null;
|
|
17
|
+
originalName: string;
|
|
18
|
+
displayName: string;
|
|
19
|
+
}>) => (props: unknown) => React.JSX.Element;
|
|
20
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import react from "react";
|
|
2
|
+
import { makeDecoratable } from "@webiny/app";
|
|
3
|
+
import { Button } from "@webiny/admin-ui";
|
|
4
|
+
import { ReactComponent } from "@webiny/icons/swap_horiz.svg";
|
|
5
|
+
import { useAuthentication } from "../../../presentation/security/hooks/useAuthentication.js";
|
|
6
|
+
import { useTenantContext } from "../../../presentation/tenancy/useTenantContext.js";
|
|
7
|
+
import { useDialogs } from "../../../components/Dialogs/useDialogs.js";
|
|
8
|
+
import { Menu } from "../../../config/AdminConfig/Menu.js";
|
|
9
|
+
const SwitchTenant = makeDecoratable("SwitchTenant", ()=>{
|
|
10
|
+
const tenancy = useTenantContext();
|
|
11
|
+
const dialogs = useDialogs();
|
|
12
|
+
const { identity, isAuthenticated } = useAuthentication();
|
|
13
|
+
if (!isAuthenticated || !identity.canSwitchTenant) return null;
|
|
14
|
+
const showTenants = ()=>{
|
|
15
|
+
const currentTenantId = identity.currentTenant.id;
|
|
16
|
+
dialogs.showDialog({
|
|
17
|
+
title: "Switch tenant",
|
|
18
|
+
description: "Your account has access to these tenants. Pick the one to open.",
|
|
19
|
+
acceptLabel: null,
|
|
20
|
+
cancelLabel: "Cancel",
|
|
21
|
+
content: /*#__PURE__*/ react.createElement("div", {
|
|
22
|
+
className: "flex flex-col gap-sm",
|
|
23
|
+
"data-testid": "switch-tenant-list"
|
|
24
|
+
}, identity.tenants.map((tenant)=>/*#__PURE__*/ react.createElement(Button, {
|
|
25
|
+
key: tenant.id,
|
|
26
|
+
variant: "tertiary",
|
|
27
|
+
className: "w-full justify-start",
|
|
28
|
+
text: tenant.id === currentTenantId ? `${tenant.name} (current)` : tenant.name,
|
|
29
|
+
disabled: tenant.id === currentTenantId,
|
|
30
|
+
onClick: ()=>tenancy.setTenant(tenant.id)
|
|
31
|
+
})))
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
return /*#__PURE__*/ react.createElement(Menu.User.Item, {
|
|
35
|
+
icon: /*#__PURE__*/ react.createElement(Menu.User.Item.Icon, {
|
|
36
|
+
element: /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
37
|
+
label: "Switch tenant"
|
|
38
|
+
}),
|
|
39
|
+
text: "Switch tenant",
|
|
40
|
+
onClick: showTenants
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
export { SwitchTenant };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=SwitchTenant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base/Base/UserMenu/SwitchTenant.js","sources":["../../../../src/base/Base/UserMenu/SwitchTenant.tsx"],"sourcesContent":["import React from \"react\";\nimport { makeDecoratable } from \"@webiny/app\";\nimport { Button } from \"@webiny/admin-ui\";\nimport { ReactComponent as SwitchIcon } from \"@webiny/icons/swap_horiz.svg\";\nimport { useAuthentication } from \"~/presentation/security/hooks/useAuthentication.js\";\nimport { useTenantContext } from \"~/presentation/tenancy/useTenantContext.js\";\nimport { useDialogs } from \"~/components/Dialogs/useDialogs.js\";\nimport { Menu } from \"~/config/AdminConfig/Menu.js\";\n\nexport const SwitchTenant = makeDecoratable(\"SwitchTenant\", () => {\n const tenancy = useTenantContext();\n const dialogs = useDialogs();\n const { identity, isAuthenticated } = useAuthentication();\n\n if (!isAuthenticated || !identity.canSwitchTenant) {\n return null;\n }\n\n const showTenants = () => {\n const currentTenantId = identity.currentTenant.id;\n\n dialogs.showDialog({\n title: \"Switch tenant\",\n description: \"Your account has access to these tenants. Pick the one to open.\",\n acceptLabel: null,\n cancelLabel: \"Cancel\",\n content: (\n <div className={\"flex flex-col gap-sm\"} data-testid={\"switch-tenant-list\"}>\n {identity.tenants.map(tenant => (\n <Button\n key={tenant.id}\n variant={\"tertiary\"}\n className={\"w-full justify-start\"}\n text={\n tenant.id === currentTenantId\n ? `${tenant.name} (current)`\n : tenant.name\n }\n disabled={tenant.id === currentTenantId}\n onClick={() => tenancy.setTenant(tenant.id)}\n />\n ))}\n </div>\n )\n });\n };\n\n return (\n <Menu.User.Item\n icon={<Menu.User.Item.Icon element={<SwitchIcon />} label={\"Switch tenant\"} />}\n text={\"Switch tenant\"}\n onClick={showTenants}\n />\n );\n});\n"],"names":["SwitchTenant","makeDecoratable","tenancy","useTenantContext","dialogs","useDialogs","identity","isAuthenticated","useAuthentication","showTenants","currentTenantId","tenant","Button","Menu","SwitchIcon"],"mappings":";;;;;;;;AASO,MAAMA,eAAeC,gBAAgB,gBAAgB;IACxD,MAAMC,UAAUC;IAChB,MAAMC,UAAUC;IAChB,MAAM,EAAEC,QAAQ,EAAEC,eAAe,EAAE,GAAGC;IAEtC,IAAI,CAACD,mBAAmB,CAACD,SAAS,eAAe,EAC7C,OAAO;IAGX,MAAMG,cAAc;QAChB,MAAMC,kBAAkBJ,SAAS,aAAa,CAAC,EAAE;QAEjDF,QAAQ,UAAU,CAAC;YACf,OAAO;YACP,aAAa;YACb,aAAa;YACb,aAAa;YACb,SAAS,WAAT,GACI,oBAAC;gBAAI,WAAW;gBAAwB,eAAa;eAChDE,SAAS,OAAO,CAAC,GAAG,CAACK,CAAAA,SAAAA,WAAAA,GAClB,oBAACC,QAAMA;oBACH,KAAKD,OAAO,EAAE;oBACd,SAAS;oBACT,WAAW;oBACX,MACIA,OAAO,EAAE,KAAKD,kBACR,GAAGC,OAAO,IAAI,CAAC,UAAU,CAAC,GAC1BA,OAAO,IAAI;oBAErB,UAAUA,OAAO,EAAE,KAAKD;oBACxB,SAAS,IAAMR,QAAQ,SAAS,CAACS,OAAO,EAAE;;QAK9D;IACJ;IAEA,OAAO,WAAP,GACI,oBAACE,KAAK,IAAI,CAAC,IAAI;QACX,oBAAM,oBAACA,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI;YAAC,uBAAS,oBAACC,gBAAUA;YAAK,OAAO;;QAC3D,MAAM;QACN,SAASL;;AAGrB"}
|
package/base/Base/UserMenu.js
CHANGED
|
@@ -3,10 +3,14 @@ import { AdminConfig } from "../../config/AdminConfig.js";
|
|
|
3
3
|
import { SignOut } from "./UserMenu/SignOut.js";
|
|
4
4
|
import { UserInfo } from "./UserMenu/UserInfo.js";
|
|
5
5
|
import { ExitTenant } from "./UserMenu/ExitTenant.js";
|
|
6
|
+
import { SwitchTenant } from "./UserMenu/SwitchTenant.js";
|
|
6
7
|
import { Menu } from "../../config/AdminConfig/Menu.js";
|
|
7
8
|
const UserMenu = /*#__PURE__*/ react.memo(()=>/*#__PURE__*/ react.createElement(AdminConfig, null, /*#__PURE__*/ react.createElement(Menu.User, {
|
|
8
9
|
name: "userInfo",
|
|
9
10
|
element: /*#__PURE__*/ react.createElement(UserInfo, null)
|
|
11
|
+
}), /*#__PURE__*/ react.createElement(Menu.User, {
|
|
12
|
+
name: "switchTenant",
|
|
13
|
+
element: /*#__PURE__*/ react.createElement(SwitchTenant, null)
|
|
10
14
|
}), /*#__PURE__*/ react.createElement(Menu.User, {
|
|
11
15
|
name: "exitTenant",
|
|
12
16
|
element: /*#__PURE__*/ react.createElement(ExitTenant, null)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base/Base/UserMenu.js","sources":["../../../src/base/Base/UserMenu.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig } from \"~/config/AdminConfig.js\";\nimport { SignOut } from \"./UserMenu/SignOut.js\";\nimport { UserInfo } from \"./UserMenu/UserInfo.js\";\nimport { ExitTenant } from \"./UserMenu/ExitTenant.js\";\nimport { Menu } from \"~/config/AdminConfig/Menu.js\";\n\nexport const UserMenu = React.memo(() => {\n return (\n <AdminConfig>\n <Menu.User name={\"userInfo\"} element={<UserInfo />} />\n <Menu.User name={\"exitTenant\"} element={<ExitTenant />} />\n <Menu.User name={\"signOut\"} element={<SignOut />} />\n </AdminConfig>\n );\n});\n\nUserMenu.displayName = \"UserMenu\";\n"],"names":["UserMenu","React","AdminConfig","Menu","UserInfo","ExitTenant","SignOut"],"mappings":"
|
|
1
|
+
{"version":3,"file":"base/Base/UserMenu.js","sources":["../../../src/base/Base/UserMenu.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig } from \"~/config/AdminConfig.js\";\nimport { SignOut } from \"./UserMenu/SignOut.js\";\nimport { UserInfo } from \"./UserMenu/UserInfo.js\";\nimport { ExitTenant } from \"./UserMenu/ExitTenant.js\";\nimport { SwitchTenant } from \"./UserMenu/SwitchTenant.js\";\nimport { Menu } from \"~/config/AdminConfig/Menu.js\";\n\nexport const UserMenu = React.memo(() => {\n return (\n <AdminConfig>\n <Menu.User name={\"userInfo\"} element={<UserInfo />} />\n <Menu.User name={\"switchTenant\"} element={<SwitchTenant />} />\n <Menu.User name={\"exitTenant\"} element={<ExitTenant />} />\n <Menu.User name={\"signOut\"} element={<SignOut />} />\n </AdminConfig>\n );\n});\n\nUserMenu.displayName = \"UserMenu\";\n"],"names":["UserMenu","React","AdminConfig","Menu","UserInfo","SwitchTenant","ExitTenant","SignOut"],"mappings":";;;;;;;AAQO,MAAMA,WAAW,WAAHA,GAAGC,MAAAA,IAAU,CAAC,IACxB,WAAP,GACI,oBAACC,aAAWA,MAAAA,WAAAA,GACR,oBAACC,KAAK,IAAI;QAAC,MAAM;QAAY,uBAAS,oBAACC,UAAQA;sBAC/C,oBAACD,KAAK,IAAI;QAAC,MAAM;QAAgB,uBAAS,oBAACE,cAAYA;sBACvD,oBAACF,KAAK,IAAI;QAAC,MAAM;QAAc,uBAAS,oBAACG,YAAUA;sBACnD,oBAACH,KAAK,IAAI;QAAC,MAAM;QAAW,uBAAS,oBAACI,SAAOA;;AAKzDP,SAAS,WAAW,GAAG"}
|
package/domain/Identity.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface IdentityData {
|
|
|
32
32
|
profile: Identity.Profile;
|
|
33
33
|
currentTenant: Tenant;
|
|
34
34
|
defaultTenant: Tenant;
|
|
35
|
+
/** The enabled tenants in which the identity has a user record, sorted by name. */
|
|
36
|
+
tenants?: Tenant[];
|
|
35
37
|
}
|
|
36
38
|
export declare class Identity {
|
|
37
39
|
private data;
|
|
@@ -48,6 +50,12 @@ export declare class Identity {
|
|
|
48
50
|
get profile(): IProfile;
|
|
49
51
|
get currentTenant(): Tenant;
|
|
50
52
|
get defaultTenant(): Tenant;
|
|
53
|
+
get tenants(): Tenant[];
|
|
54
|
+
/**
|
|
55
|
+
* Whether the identity can open another tenant than this one.
|
|
56
|
+
* Root users never can: they open tenants through Tenant Manager.
|
|
57
|
+
*/
|
|
58
|
+
get canSwitchTenant(): boolean;
|
|
51
59
|
getPermission<T extends Identity.Permission = Identity.Permission>(name: string, exact?: boolean): T | null;
|
|
52
60
|
getPermissions<T extends Identity.Permission = Identity.Permission>(name?: string): Array<T>;
|
|
53
61
|
update(updates: Partial<Pick<Identity.Data, "displayName" | "profile">>): void;
|
package/domain/Identity.js
CHANGED
|
@@ -58,6 +58,12 @@ class Identity {
|
|
|
58
58
|
get defaultTenant() {
|
|
59
59
|
return this.data.defaultTenant;
|
|
60
60
|
}
|
|
61
|
+
get tenants() {
|
|
62
|
+
return this.data.tenants ?? [];
|
|
63
|
+
}
|
|
64
|
+
get canSwitchTenant() {
|
|
65
|
+
return this.tenants.length > 1 && !this.tenants.some((tenant)=>"root" === tenant.id);
|
|
66
|
+
}
|
|
61
67
|
getPermission(name, exact) {
|
|
62
68
|
const perms = this.data.permissions || [];
|
|
63
69
|
const exactMatch = perms.find((p)=>p.name === name);
|
package/domain/Identity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain/Identity.js","sources":["../../src/domain/Identity.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from \"mobx\";\nimport { minimatch } from \"minimatch\";\nimport type { Tenant } from \"~/features/tenancy/abstractions.js\";\n\nexport interface IPermission {\n name: string;\n [key: string]: any;\n}\n\nexport interface IRole {\n id: string;\n slug: string;\n name: string;\n}\n\nexport interface ITeam {\n id: string;\n slug: string;\n name: string;\n}\n\nexport interface IProfile {\n external: boolean;\n email?: string;\n firstName?: string;\n lastName?: string;\n avatar?: {\n src?: string;\n };\n}\n\nexport interface IdentityData {\n id: string;\n type: string;\n displayName: string;\n roles: IRole[];\n teams: ITeam[];\n permissions: Identity.Permission[];\n profile: Identity.Profile;\n currentTenant: Tenant;\n defaultTenant: Tenant;\n}\n\nconst anonymousData: IdentityData = {\n id: \"anonymous\",\n displayName: \"Anonymous\",\n type: \"admin\",\n roles: [],\n teams: [],\n permissions: [],\n profile: {\n external: false\n },\n currentTenant: {\n id: \"root\",\n name: \"Root\"\n },\n defaultTenant: {\n id: \"root\",\n name: \"Root\"\n }\n};\n\nexport class Identity {\n private constructor(\n private data: Identity.Data,\n private identityType: \"anonymous\" | \"authenticated\"\n ) {\n makeAutoObservable(this);\n }\n\n static createAuthenticated(data: Identity.Data) {\n return new Identity(data, \"authenticated\");\n }\n\n static createAnonymous() {\n return new Identity(anonymousData, \"anonymous\");\n }\n\n get isAuthenticated(): boolean {\n return this.identityType === \"authenticated\";\n }\n\n get id() {\n return this.data.id;\n }\n\n get type() {\n return this.data.type;\n }\n\n get displayName() {\n return this.data.displayName;\n }\n\n get roles() {\n return this.data.roles ?? [];\n }\n\n get teams() {\n return this.data.teams ?? [];\n }\n\n get profile() {\n return this.data.profile;\n }\n\n get currentTenant() {\n return this.data.currentTenant;\n }\n\n get defaultTenant() {\n return this.data.defaultTenant;\n }\n\n getPermission<T extends Identity.Permission = Identity.Permission>(\n name: string,\n exact?: boolean\n ): T | null {\n const perms = (this.data.permissions || []) as T[];\n const exactMatch = perms.find(p => p.name === name);\n if (exactMatch) {\n return exactMatch as T;\n } else if (exact) {\n return null;\n }\n\n // Try matching using patterns\n return perms.find(p => minimatch(name, p.name)) || null;\n }\n\n getPermissions<T extends Identity.Permission = Identity.Permission>(name?: string): Array<T> {\n const permissions = this.data.permissions || [];\n if (!name) {\n return permissions as T[];\n }\n\n return permissions.filter(current => {\n const exactMatch = current.name === name;\n if (exactMatch) {\n return true;\n }\n\n // Try matching using patterns.\n return minimatch(name, current.name);\n }) as T[];\n }\n\n update(updates: Partial<Pick<Identity.Data, \"displayName\" | \"profile\">>) {\n runInAction(() => {\n this.data.displayName = updates.displayName || this.data.displayName;\n this.data.profile = updates.profile || this.data.profile;\n });\n }\n}\n\nexport namespace Identity {\n export type Data = IdentityData;\n export type Permission = IPermission;\n export type Profile = IProfile;\n}\n"],"names":["anonymousData","Identity","data","identityType","makeAutoObservable","name","exact","perms","exactMatch","p","minimatch","permissions","current","updates","runInAction"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"domain/Identity.js","sources":["../../src/domain/Identity.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from \"mobx\";\nimport { minimatch } from \"minimatch\";\nimport type { Tenant } from \"~/features/tenancy/abstractions.js\";\n\nexport interface IPermission {\n name: string;\n [key: string]: any;\n}\n\nexport interface IRole {\n id: string;\n slug: string;\n name: string;\n}\n\nexport interface ITeam {\n id: string;\n slug: string;\n name: string;\n}\n\nexport interface IProfile {\n external: boolean;\n email?: string;\n firstName?: string;\n lastName?: string;\n avatar?: {\n src?: string;\n };\n}\n\nexport interface IdentityData {\n id: string;\n type: string;\n displayName: string;\n roles: IRole[];\n teams: ITeam[];\n permissions: Identity.Permission[];\n profile: Identity.Profile;\n currentTenant: Tenant;\n defaultTenant: Tenant;\n /** The enabled tenants in which the identity has a user record, sorted by name. */\n tenants?: Tenant[];\n}\n\nconst anonymousData: IdentityData = {\n id: \"anonymous\",\n displayName: \"Anonymous\",\n type: \"admin\",\n roles: [],\n teams: [],\n permissions: [],\n profile: {\n external: false\n },\n currentTenant: {\n id: \"root\",\n name: \"Root\"\n },\n defaultTenant: {\n id: \"root\",\n name: \"Root\"\n }\n};\n\nexport class Identity {\n private constructor(\n private data: Identity.Data,\n private identityType: \"anonymous\" | \"authenticated\"\n ) {\n makeAutoObservable(this);\n }\n\n static createAuthenticated(data: Identity.Data) {\n return new Identity(data, \"authenticated\");\n }\n\n static createAnonymous() {\n return new Identity(anonymousData, \"anonymous\");\n }\n\n get isAuthenticated(): boolean {\n return this.identityType === \"authenticated\";\n }\n\n get id() {\n return this.data.id;\n }\n\n get type() {\n return this.data.type;\n }\n\n get displayName() {\n return this.data.displayName;\n }\n\n get roles() {\n return this.data.roles ?? [];\n }\n\n get teams() {\n return this.data.teams ?? [];\n }\n\n get profile() {\n return this.data.profile;\n }\n\n get currentTenant() {\n return this.data.currentTenant;\n }\n\n get defaultTenant() {\n return this.data.defaultTenant;\n }\n\n get tenants(): Tenant[] {\n return this.data.tenants ?? [];\n }\n\n /**\n * Whether the identity can open another tenant than this one.\n * Root users never can: they open tenants through Tenant Manager.\n */\n get canSwitchTenant(): boolean {\n return this.tenants.length > 1 && !this.tenants.some(tenant => tenant.id === \"root\");\n }\n\n getPermission<T extends Identity.Permission = Identity.Permission>(\n name: string,\n exact?: boolean\n ): T | null {\n const perms = (this.data.permissions || []) as T[];\n const exactMatch = perms.find(p => p.name === name);\n if (exactMatch) {\n return exactMatch as T;\n } else if (exact) {\n return null;\n }\n\n // Try matching using patterns\n return perms.find(p => minimatch(name, p.name)) || null;\n }\n\n getPermissions<T extends Identity.Permission = Identity.Permission>(name?: string): Array<T> {\n const permissions = this.data.permissions || [];\n if (!name) {\n return permissions as T[];\n }\n\n return permissions.filter(current => {\n const exactMatch = current.name === name;\n if (exactMatch) {\n return true;\n }\n\n // Try matching using patterns.\n return minimatch(name, current.name);\n }) as T[];\n }\n\n update(updates: Partial<Pick<Identity.Data, \"displayName\" | \"profile\">>) {\n runInAction(() => {\n this.data.displayName = updates.displayName || this.data.displayName;\n this.data.profile = updates.profile || this.data.profile;\n });\n }\n}\n\nexport namespace Identity {\n export type Data = IdentityData;\n export type Permission = IPermission;\n export type Profile = IProfile;\n}\n"],"names":["anonymousData","Identity","data","identityType","makeAutoObservable","tenant","name","exact","perms","exactMatch","p","minimatch","permissions","current","updates","runInAction"],"mappings":";;AA6CA,MAAMA,gBAA8B;IAChC,IAAI;IACJ,aAAa;IACb,MAAM;IACN,OAAO,EAAE;IACT,OAAO,EAAE;IACT,aAAa,EAAE;IACf,SAAS;QACL,UAAU;IACd;IACA,eAAe;QACX,IAAI;QACJ,MAAM;IACV;IACA,eAAe;QACX,IAAI;QACJ,MAAM;IACV;AACJ;AAEO,MAAMC;IACT,YACYC,IAAmB,EACnBC,YAA2C,CACrD;aAFUD,IAAI,GAAJA;aACAC,YAAY,GAAZA;QAERC,mBAAmB,IAAI;IAC3B;IAEA,OAAO,oBAAoBF,IAAmB,EAAE;QAC5C,OAAO,IAAID,SAASC,MAAM;IAC9B;IAEA,OAAO,kBAAkB;QACrB,OAAO,IAAID,SAASD,eAAe;IACvC;IAEA,IAAI,kBAA2B;QAC3B,OAAO,AAAsB,oBAAtB,IAAI,CAAC,YAAY;IAC5B;IAEA,IAAI,KAAK;QACL,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE;IACvB;IAEA,IAAI,OAAO;QACP,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI;IACzB;IAEA,IAAI,cAAc;QACd,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW;IAChC;IAEA,IAAI,QAAQ;QACR,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;IAChC;IAEA,IAAI,QAAQ;QACR,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;IAChC;IAEA,IAAI,UAAU;QACV,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO;IAC5B;IAEA,IAAI,gBAAgB;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa;IAClC;IAEA,IAAI,gBAAgB;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa;IAClC;IAEA,IAAI,UAAoB;QACpB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE;IAClC;IAMA,IAAI,kBAA2B;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAACK,CAAAA,SAAUA,AAAc,WAAdA,OAAO,EAAE;IAC5E;IAEA,cACIC,IAAY,EACZC,KAAe,EACP;QACR,MAAMC,QAAS,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE;QAC1C,MAAMC,aAAaD,MAAM,IAAI,CAACE,CAAAA,IAAKA,EAAE,IAAI,KAAKJ;QAC9C,IAAIG,YACA,OAAOA;aACJ,IAAIF,OACP,OAAO;QAIX,OAAOC,MAAM,IAAI,CAACE,CAAAA,IAAKC,UAAUL,MAAMI,EAAE,IAAI,MAAM;IACvD;IAEA,eAAoEJ,IAAa,EAAY;QACzF,MAAMM,cAAc,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE;QAC/C,IAAI,CAACN,MACD,OAAOM;QAGX,OAAOA,YAAY,MAAM,CAACC,CAAAA;YACtB,MAAMJ,aAAaI,QAAQ,IAAI,KAAKP;YACpC,IAAIG,YACA,OAAO;YAIX,OAAOE,UAAUL,MAAMO,QAAQ,IAAI;QACvC;IACJ;IAEA,OAAOC,OAAgE,EAAE;QACrEC,YAAY;YACR,IAAI,CAAC,IAAI,CAAC,WAAW,GAAGD,QAAQ,WAAW,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW;YACpE,IAAI,CAAC,IAAI,CAAC,OAAO,GAAGA,QAAQ,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO;QAC5D;IACJ;AACJ"}
|
|
@@ -4,7 +4,8 @@ export { createPermissionsFeature } from "../../permissions/index.js";
|
|
|
4
4
|
export { createHasPermission } from "../../permissions/index.js";
|
|
5
5
|
export { createUsePermissions } from "../../permissions/index.js";
|
|
6
6
|
export type { Permissions } from "../../permissions/types.js";
|
|
7
|
-
export { LogInUseCase } from "../../features/security/LogIn/index.js";
|
|
7
|
+
export { LogInUseCase, LogInError } from "../../features/security/LogIn/index.js";
|
|
8
|
+
export { LoginTenantResolver } from "../../features/security/LoginTenantResolver/index.js";
|
|
8
9
|
export { LogOutUseCase } from "../../features/security/LogOut/index.js";
|
|
9
10
|
export { AuthenticationContext } from "../../features/security/AuthenticationContext/index.js";
|
|
10
11
|
export { IdentityContext } from "../../features/security/IdentityContext/index.js";
|
|
@@ -12,3 +13,5 @@ export { useAuthentication } from "../../presentation/security/hooks/useAuthenti
|
|
|
12
13
|
export { useIdentity } from "../../presentation/security/hooks/useIdentity.js";
|
|
13
14
|
export { HasPermissionComponent } from "../../presentation/security/components/HasPermission.js";
|
|
14
15
|
export { LoginScreenComponent } from "../../base/ui/LoginScreen.js";
|
|
16
|
+
export { LoginTenantPicker } from "../../presentation/security/components/LoginTenantPicker.js";
|
|
17
|
+
export { LoginNoTenantAccess } from "../../presentation/security/components/LoginNoTenantAccess.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { createHasPermission, createPermissionSchema, createPermissionsAbstraction, createPermissionsFeature, createUsePermissions } from "../../permissions/index.js";
|
|
2
|
-
export { LogInUseCase } from "../../features/security/LogIn/index.js";
|
|
2
|
+
export { LogInError, LogInUseCase } from "../../features/security/LogIn/index.js";
|
|
3
|
+
export { LoginTenantResolver } from "../../features/security/LoginTenantResolver/index.js";
|
|
3
4
|
export { LogOutUseCase } from "../../features/security/LogOut/index.js";
|
|
4
5
|
export { AuthenticationContext } from "../../features/security/AuthenticationContext/index.js";
|
|
5
6
|
export { IdentityContext } from "../../features/security/IdentityContext/index.js";
|
|
@@ -7,3 +8,5 @@ export { useAuthentication } from "../../presentation/security/hooks/useAuthenti
|
|
|
7
8
|
export { useIdentity } from "../../presentation/security/hooks/useIdentity.js";
|
|
8
9
|
export { HasPermissionComponent } from "../../presentation/security/components/HasPermission.js";
|
|
9
10
|
export { LoginScreenComponent } from "../../base/ui/LoginScreen.js";
|
|
11
|
+
export { LoginTenantPicker } from "../../presentation/security/components/LoginTenantPicker.js";
|
|
12
|
+
export { LoginNoTenantAccess } from "../../presentation/security/components/LoginNoTenantAccess.js";
|