From 22fb620eefb541583ee339b8847604ea79ae0087 Mon Sep 17 00:00:00 2001 From: louiscklaw Date: Wed, 4 Jun 2025 02:35:32 +0800 Subject: [PATCH] "chore: update frontend dev script to include lint checks and add ESLint config file" --- 03_source/frontend/dev.sh | 6 +- 03_source/frontend/eslint-show-config.json | 1721 +++++++++++++++++ 03_source/frontend/package.json | 1 + 03_source/frontend/src/actions/invoice.ts | 4 +- 03_source/frontend/src/actions/product.ts | 1 + .../frontend/src/auth/context/jwt/action.ts | 4 +- .../frontend/src/auth/context/jwt/utils.ts | 4 +- .../src/pages/dashboard/user/edit.tsx | 2 +- .../sections/calendar/calendar-toolbar.tsx | 3 +- .../src/sections/invoice/invoice-details.tsx | 25 +- .../invoice/invoice-new-edit-details.tsx | 6 +- .../invoice/invoice-new-edit-form.tsx | 4 + .../src/sections/invoice/invoice-toolbar.tsx | 2 +- 03_source/frontend/src/types/common.ts | 2 +- 03_source/frontend/src/types/invoice.ts | 26 +- 03_source/frontend/yarn.lock | 197 +- 03_source/mobile/.prettierrc | 2 +- 03_source/mobile/package-lock.json | 318 ++- 03_source/mobile/package.json | 6 +- 03_source/mobile/src/App.tsx | 22 +- 03_source/mobile/src/AppRoute.tsx | 27 +- 03_source/mobile/src/TabAppRoute.tsx | 7 +- .../mobile/src/components/HomeOrTutorial.tsx | 6 +- .../mobile/src/components/RedirectToLogin.tsx | 7 +- 03_source/mobile/src/context/action.tsx | 0 03_source/mobile/src/context/index.ts | 0 03_source/mobile/src/context/jwt/action.tsx | 92 + 03_source/mobile/src/context/jwt/constant.ts | 1 + 03_source/mobile/src/context/jwt/utils.tsx | 97 + 03_source/mobile/src/data/AppContext.tsx | 4 +- 03_source/mobile/src/data/dataApi.ts | 28 +- 03_source/mobile/src/data/state.ts | 2 + .../mobile/src/data/user/user.actions.ts | 63 +- .../mobile/src/data/user/user.reducer.ts | 6 + 03_source/mobile/src/data/user/user.state.ts | 3 + 03_source/mobile/src/global-config.ts | 3 + 03_source/mobile/src/hooks/use-set-state.ts | 32 + .../mobile/src/pages/Helloworld/index.tsx | 27 + 03_source/mobile/src/pages/MainTabs.tsx | 4 +- .../mobile/src/pages/MyLogin/endpoints.ts | 12 + 03_source/mobile/src/pages/MyLogin/index.tsx | 269 +++ .../mobile/src/pages/MyLogin/isValidToken.tsx | 22 + .../mobile/src/pages/MyLogin/jwtDecode.tsx | 19 + 03_source/mobile/src/pages/MyLogin/style.scss | 23 + .../src/pages/MyProfile/NotLoggedIn/index.tsx | 38 +- 03_source/mobile/src/pages/SpeakerDetail.tsx | 28 +- 03_source/mobile/src/paths.ts | 12 +- 03_source/mobile/yarn.lock | 224 ++- 48 files changed, 3315 insertions(+), 97 deletions(-) create mode 100644 03_source/frontend/eslint-show-config.json create mode 100644 03_source/mobile/src/context/action.tsx create mode 100644 03_source/mobile/src/context/index.ts create mode 100644 03_source/mobile/src/context/jwt/action.tsx create mode 100644 03_source/mobile/src/context/jwt/constant.ts create mode 100644 03_source/mobile/src/context/jwt/utils.tsx create mode 100644 03_source/mobile/src/global-config.ts create mode 100644 03_source/mobile/src/hooks/use-set-state.ts create mode 100644 03_source/mobile/src/pages/Helloworld/index.tsx create mode 100644 03_source/mobile/src/pages/MyLogin/endpoints.ts create mode 100644 03_source/mobile/src/pages/MyLogin/index.tsx create mode 100644 03_source/mobile/src/pages/MyLogin/isValidToken.tsx create mode 100644 03_source/mobile/src/pages/MyLogin/jwtDecode.tsx create mode 100644 03_source/mobile/src/pages/MyLogin/style.scss diff --git a/03_source/frontend/dev.sh b/03_source/frontend/dev.sh index 8babd49..9ca2691 100755 --- a/03_source/frontend/dev.sh +++ b/03_source/frontend/dev.sh @@ -1,7 +1,11 @@ #!/usr/bin/env bash +yarn --dev + while true; do - yarn --dev + + # yarn tsc:print + yarn lint:print yarn dev --force --clearScreen diff --git a/03_source/frontend/eslint-show-config.json b/03_source/frontend/eslint-show-config.json new file mode 100644 index 0000000..56933eb --- /dev/null +++ b/03_source/frontend/eslint-show-config.json @@ -0,0 +1,1721 @@ +{ + "settings": { + "react": { + "version": "detect" + }, + "import/extensions": [ + ".ts", + ".cts", + ".mts", + ".tsx", + ".js", + ".jsx", + ".mjs", + ".cjs" + ], + "import/external-module-folders": [ + "node_modules", + "node_modules/@types" + ], + "import/parsers": { + "@typescript-eslint/parser": [ + ".ts", + ".cts", + ".mts", + ".tsx" + ] + }, + "import/resolver": { + "node": { + "extensions": [ + ".ts", + ".cts", + ".mts", + ".tsx", + ".js", + ".jsx", + ".mjs", + ".cjs" + ] + }, + "typescript": { + "project": "./tsconfig.json" + } + } + }, + "linterOptions": { + "reportUnusedDisableDirectives": 1 + }, + "rules": { + "constructor-super": [ + 2 + ], + "for-direction": [ + 2 + ], + "getter-return": [ + 2, + { + "allowImplicit": false + } + ], + "no-async-promise-executor": [ + 2 + ], + "no-case-declarations": [ + 2 + ], + "no-class-assign": [ + 2 + ], + "no-compare-neg-zero": [ + 2 + ], + "no-cond-assign": [ + 2, + "except-parens" + ], + "no-const-assign": [ + 2 + ], + "no-constant-binary-expression": [ + 2 + ], + "no-constant-condition": [ + 1, + { + "checkLoops": "allExceptWhileTrue" + } + ], + "no-control-regex": [ + 2 + ], + "no-debugger": [ + 2 + ], + "no-delete-var": [ + 2 + ], + "no-dupe-args": [ + 2 + ], + "no-dupe-class-members": [ + 2 + ], + "no-dupe-else-if": [ + 2 + ], + "no-dupe-keys": [ + 2 + ], + "no-duplicate-case": [ + 2 + ], + "no-empty": [ + 2, + { + "allowEmptyCatch": false + } + ], + "no-empty-character-class": [ + 2 + ], + "no-empty-pattern": [ + 2, + { + "allowObjectPatternsAsParameters": false + } + ], + "no-empty-static-block": [ + 2 + ], + "no-ex-assign": [ + 2 + ], + "no-extra-boolean-cast": [ + 2, + {} + ], + "no-fallthrough": [ + 2, + { + "allowEmptyCase": false, + "reportUnusedFallthroughComment": false + } + ], + "no-func-assign": [ + 2 + ], + "no-global-assign": [ + 2, + { + "exceptions": [] + } + ], + "no-import-assign": [ + 2 + ], + "no-invalid-regexp": [ + 2, + {} + ], + "no-irregular-whitespace": [ + 2, + { + "skipComments": false, + "skipJSXText": false, + "skipRegExps": false, + "skipStrings": true, + "skipTemplates": false + } + ], + "no-loss-of-precision": [ + 2 + ], + "no-misleading-character-class": [ + 2 + ], + "no-new-native-nonconstructor": [ + 2 + ], + "no-nonoctal-decimal-escape": [ + 2 + ], + "no-obj-calls": [ + 2 + ], + "no-octal": [ + 2 + ], + "no-prototype-builtins": [ + 2 + ], + "no-redeclare": [ + 2, + { + "builtinGlobals": true + } + ], + "no-regex-spaces": [ + 2 + ], + "no-self-assign": [ + 2, + { + "props": true + } + ], + "no-setter-return": [ + 2 + ], + "no-shadow-restricted-names": [ + 2 + ], + "no-sparse-arrays": [ + 2 + ], + "no-this-before-super": [ + 2 + ], + "no-undef": [ + 2, + { + "typeof": false + } + ], + "no-unexpected-multiline": [ + 2 + ], + "no-unreachable": [ + 2 + ], + "no-unsafe-finally": [ + 2 + ], + "no-unsafe-negation": [ + 2, + { + "enforceForOrderingRelations": false + } + ], + "no-unsafe-optional-chaining": [ + 2, + { + "disallowArithmeticOperators": false + } + ], + "no-unused-labels": [ + 2 + ], + "no-unused-private-class-members": [ + 2 + ], + "no-unused-vars": [ + 0 + ], + "no-useless-backreference": [ + 2 + ], + "no-useless-catch": [ + 2 + ], + "no-useless-escape": [ + 2 + ], + "no-with": [ + 2 + ], + "require-yield": [ + 2 + ], + "use-isnan": [ + 2, + { + "enforceForIndexOf": false, + "enforceForSwitchCase": true + } + ], + "valid-typeof": [ + 2, + { + "requireStringLiterals": false + } + ], + "@typescript-eslint/ban-ts-comment": [ + 2 + ], + "no-array-constructor": [ + 0 + ], + "@typescript-eslint/no-array-constructor": [ + 2 + ], + "@typescript-eslint/no-duplicate-enum-values": [ + 2 + ], + "@typescript-eslint/no-empty-object-type": [ + 0 + ], + "@typescript-eslint/no-explicit-any": [ + 0 + ], + "@typescript-eslint/no-extra-non-null-assertion": [ + 2 + ], + "@typescript-eslint/no-misused-new": [ + 2 + ], + "@typescript-eslint/no-namespace": [ + 2 + ], + "@typescript-eslint/no-non-null-asserted-optional-chain": [ + 2 + ], + "@typescript-eslint/no-require-imports": [ + 2 + ], + "@typescript-eslint/no-this-alias": [ + 2 + ], + "@typescript-eslint/no-unnecessary-type-constraint": [ + 2 + ], + "@typescript-eslint/no-unsafe-declaration-merging": [ + 2 + ], + "@typescript-eslint/no-unsafe-function-type": [ + 2 + ], + "no-unused-expressions": [ + 0, + { + "allowShortCircuit": false, + "allowTernary": false, + "allowTaggedTemplates": false, + "enforceForJSX": false + } + ], + "@typescript-eslint/no-unused-expressions": [ + 2, + { + "allowShortCircuit": false, + "allowTaggedTemplates": false, + "allowTernary": false + } + ], + "@typescript-eslint/no-unused-vars": [ + 1, + { + "args": "none" + } + ], + "@typescript-eslint/no-wrapper-object-types": [ + 2 + ], + "@typescript-eslint/prefer-as-const": [ + 2 + ], + "@typescript-eslint/prefer-namespace-keyword": [ + 2 + ], + "@typescript-eslint/triple-slash-reference": [ + 2 + ], + "react/display-name": [ + 0 + ], + "react/jsx-key": [ + 0 + ], + "react/jsx-no-comment-textnodes": [ + 2 + ], + "react/jsx-no-duplicate-props": [ + 2 + ], + "react/jsx-no-target-blank": [ + 2 + ], + "react/jsx-no-undef": [ + 2 + ], + "react/jsx-uses-react": [ + 2 + ], + "react/jsx-uses-vars": [ + 2 + ], + "react/no-children-prop": [ + 0 + ], + "react/no-danger-with-children": [ + 2 + ], + "react/no-deprecated": [ + 2 + ], + "react/no-direct-mutation-state": [ + 2 + ], + "react/no-find-dom-node": [ + 2 + ], + "react/no-is-mounted": [ + 2 + ], + "react/no-render-return-value": [ + 2 + ], + "react/no-string-refs": [ + 2 + ], + "react/no-unescaped-entities": [ + 2 + ], + "react/no-unknown-property": [ + 2 + ], + "react/no-unsafe": [ + 0 + ], + "react/prop-types": [ + 0 + ], + "react/react-in-jsx-scope": [ + 0 + ], + "react/require-render-return": [ + 2 + ], + "react-hooks/rules-of-hooks": [ + 2 + ], + "react-hooks/exhaustive-deps": [ + 1 + ], + "func-names": [ + 1, + "always", + {} + ], + "no-bitwise": [ + 2, + { + "allow": [], + "int32Hint": false + } + ], + "object-shorthand": [ + 1 + ], + "no-useless-rename": [ + 1, + { + "ignoreDestructuring": false, + "ignoreImport": false, + "ignoreExport": false + } + ], + "default-case-last": [ + 2 + ], + "consistent-return": [ + 2, + { + "treatUndefinedAsUnspecified": false + } + ], + "default-case": [ + 2, + { + "commentPattern": "^no default$" + } + ], + "lines-around-directive": [ + 2, + { + "before": "always", + "after": "always" + } + ], + "arrow-body-style": [ + 2, + "as-needed", + { + "requireReturnForObjectLiteral": false + } + ], + "react/jsx-boolean-value": [ + 2 + ], + "react/self-closing-comp": [ + 2 + ], + "react/jsx-no-useless-fragment": [ + 1, + { + "allowExpressions": true + } + ], + "react/jsx-curly-brace-presence": [ + 2, + { + "props": "never", + "children": "never" + } + ], + "@typescript-eslint/no-shadow": [ + 2 + ], + "@typescript-eslint/consistent-type-imports": [ + 1 + ], + "import/no-unresolved": [ + 2 + ], + "import/named": [ + 0 + ], + "import/namespace": [ + 0 + ], + "import/default": [ + 0 + ], + "import/export": [ + 0 + ], + "import/no-named-as-default": [ + 0 + ], + "import/no-named-as-default-member": [ + 0 + ], + "import/no-duplicates": [ + 1 + ], + "import/newline-after-import": [ + 2 + ], + "import/no-cycle": [ + 0, + { + "maxDepth": "∞", + "ignoreExternal": false, + "allowUnsafeDynamicCyclicDependency": false + } + ], + "unused-imports/no-unused-imports": [ + 1 + ], + "unused-imports/no-unused-vars": [ + 0, + { + "vars": "all", + "varsIgnorePattern": "^_", + "args": "after-used", + "argsIgnorePattern": "^_" + } + ] + }, + "plugins": [ + "@", + "@typescript-eslint:@typescript-eslint/eslint-plugin@8.28.0", + "react", + "react-hooks:eslint-plugin-react-hooks", + "unused-imports:unused-imports", + "perfectionist:perfectionist", + "import" + ], + "language": "@/js", + "languageOptions": { + "sourceType": "module", + "ecmaVersion": 2025, + "parser": "typescript-eslint/parser@8.28.0", + "parserOptions": { + "ecmaFeatures": { + "jsx": true + } + }, + "globals": { + "AbortController": false, + "AbortSignal": false, + "AbsoluteOrientationSensor": false, + "AbstractRange": false, + "Accelerometer": false, + "addEventListener": false, + "ai": false, + "AI": false, + "AITextSession": false, + "alert": false, + "AnalyserNode": false, + "Animation": false, + "AnimationEffect": false, + "AnimationEvent": false, + "AnimationPlaybackEvent": false, + "AnimationTimeline": false, + "atob": false, + "Attr": false, + "Audio": false, + "AudioBuffer": false, + "AudioBufferSourceNode": false, + "AudioContext": false, + "AudioData": false, + "AudioDecoder": false, + "AudioDestinationNode": false, + "AudioEncoder": false, + "AudioListener": false, + "AudioNode": false, + "AudioParam": false, + "AudioParamMap": false, + "AudioProcessingEvent": false, + "AudioScheduledSourceNode": false, + "AudioSinkInfo": false, + "AudioWorklet": false, + "AudioWorkletGlobalScope": false, + "AudioWorkletNode": false, + "AudioWorkletProcessor": false, + "AuthenticatorAssertionResponse": false, + "AuthenticatorAttestationResponse": false, + "AuthenticatorResponse": false, + "BackgroundFetchManager": false, + "BackgroundFetchRecord": false, + "BackgroundFetchRegistration": false, + "BarcodeDetector": false, + "BarProp": false, + "BaseAudioContext": false, + "BatteryManager": false, + "BeforeUnloadEvent": false, + "BiquadFilterNode": false, + "Blob": false, + "BlobEvent": false, + "Bluetooth": false, + "BluetoothCharacteristicProperties": false, + "BluetoothDevice": false, + "BluetoothRemoteGATTCharacteristic": false, + "BluetoothRemoteGATTDescriptor": false, + "BluetoothRemoteGATTServer": false, + "BluetoothRemoteGATTService": false, + "BluetoothUUID": false, + "blur": false, + "BroadcastChannel": false, + "BrowserCaptureMediaStreamTrack": false, + "btoa": false, + "ByteLengthQueuingStrategy": false, + "Cache": false, + "caches": false, + "CacheStorage": false, + "cancelAnimationFrame": false, + "cancelIdleCallback": false, + "CanvasCaptureMediaStream": false, + "CanvasCaptureMediaStreamTrack": false, + "CanvasGradient": false, + "CanvasPattern": false, + "CanvasRenderingContext2D": false, + "CaptureController": false, + "CaretPosition": false, + "CDATASection": false, + "ChannelMergerNode": false, + "ChannelSplitterNode": false, + "ChapterInformation": false, + "CharacterBoundsUpdateEvent": false, + "CharacterData": false, + "clearInterval": false, + "clearTimeout": false, + "clientInformation": false, + "Clipboard": false, + "ClipboardEvent": false, + "ClipboardItem": false, + "close": false, + "closed": false, + "CloseEvent": false, + "CloseWatcher": false, + "Comment": false, + "CompositionEvent": false, + "CompressionStream": false, + "confirm": false, + "console": false, + "ConstantSourceNode": false, + "ContentVisibilityAutoStateChangeEvent": false, + "ConvolverNode": false, + "CookieChangeEvent": false, + "CookieDeprecationLabel": false, + "cookieStore": false, + "CookieStore": false, + "CookieStoreManager": false, + "CountQueuingStrategy": false, + "createImageBitmap": false, + "Credential": false, + "credentialless": false, + "CredentialsContainer": false, + "CropTarget": false, + "crossOriginIsolated": false, + "crypto": false, + "Crypto": false, + "CryptoKey": false, + "CSS": false, + "CSSAnimation": false, + "CSSConditionRule": false, + "CSSContainerRule": false, + "CSSCounterStyleRule": false, + "CSSFontFaceRule": false, + "CSSFontFeatureValuesRule": false, + "CSSFontPaletteValuesRule": false, + "CSSGroupingRule": false, + "CSSImageValue": false, + "CSSImportRule": false, + "CSSKeyframeRule": false, + "CSSKeyframesRule": false, + "CSSKeywordValue": false, + "CSSLayerBlockRule": false, + "CSSLayerStatementRule": false, + "CSSMarginRule": false, + "CSSMathClamp": false, + "CSSMathInvert": false, + "CSSMathMax": false, + "CSSMathMin": false, + "CSSMathNegate": false, + "CSSMathProduct": false, + "CSSMathSum": false, + "CSSMathValue": false, + "CSSMatrixComponent": false, + "CSSMediaRule": false, + "CSSNamespaceRule": false, + "CSSNestedDeclarations": false, + "CSSNumericArray": false, + "CSSNumericValue": false, + "CSSPageDescriptors": false, + "CSSPageRule": false, + "CSSPerspective": false, + "CSSPositionTryDescriptors": false, + "CSSPositionTryRule": false, + "CSSPositionValue": false, + "CSSPropertyRule": false, + "CSSRotate": false, + "CSSRule": false, + "CSSRuleList": false, + "CSSScale": false, + "CSSScopeRule": false, + "CSSSkew": false, + "CSSSkewX": false, + "CSSSkewY": false, + "CSSStartingStyleRule": false, + "CSSStyleDeclaration": false, + "CSSStyleRule": false, + "CSSStyleSheet": false, + "CSSStyleValue": false, + "CSSSupportsRule": false, + "CSSTransformComponent": false, + "CSSTransformValue": false, + "CSSTransition": false, + "CSSTranslate": false, + "CSSUnitValue": false, + "CSSUnparsedValue": false, + "CSSVariableReferenceValue": false, + "CSSViewTransitionRule": false, + "currentFrame": false, + "currentTime": false, + "CustomElementRegistry": false, + "customElements": false, + "CustomEvent": false, + "CustomStateSet": false, + "DataTransfer": false, + "DataTransferItem": false, + "DataTransferItemList": false, + "DecompressionStream": false, + "DelayNode": false, + "DelegatedInkTrailPresenter": false, + "DeviceMotionEvent": false, + "DeviceMotionEventAcceleration": false, + "DeviceMotionEventRotationRate": false, + "DeviceOrientationEvent": false, + "devicePixelRatio": false, + "dispatchEvent": false, + "document": false, + "Document": false, + "DocumentFragment": false, + "documentPictureInPicture": false, + "DocumentPictureInPicture": false, + "DocumentPictureInPictureEvent": false, + "DocumentTimeline": false, + "DocumentType": false, + "DOMError": false, + "DOMException": false, + "DOMImplementation": false, + "DOMMatrix": false, + "DOMMatrixReadOnly": false, + "DOMParser": false, + "DOMPoint": false, + "DOMPointReadOnly": false, + "DOMQuad": false, + "DOMRect": false, + "DOMRectList": false, + "DOMRectReadOnly": false, + "DOMStringList": false, + "DOMStringMap": false, + "DOMTokenList": false, + "DragEvent": false, + "DynamicsCompressorNode": false, + "EditContext": false, + "Element": false, + "ElementInternals": false, + "EncodedAudioChunk": false, + "EncodedVideoChunk": false, + "ErrorEvent": false, + "event": false, + "Event": false, + "EventCounts": false, + "EventSource": false, + "EventTarget": false, + "external": false, + "External": false, + "EyeDropper": false, + "FeaturePolicy": false, + "FederatedCredential": false, + "fence": false, + "Fence": false, + "FencedFrameConfig": false, + "fetch": false, + "fetchLater": false, + "FetchLaterResult": false, + "File": false, + "FileList": false, + "FileReader": false, + "FileSystem": false, + "FileSystemDirectoryEntry": false, + "FileSystemDirectoryHandle": false, + "FileSystemDirectoryReader": false, + "FileSystemEntry": false, + "FileSystemFileEntry": false, + "FileSystemFileHandle": false, + "FileSystemHandle": false, + "FileSystemWritableFileStream": false, + "find": false, + "Float16Array": false, + "focus": false, + "FocusEvent": false, + "FontData": false, + "FontFace": false, + "FontFaceSet": false, + "FontFaceSetLoadEvent": false, + "FormData": false, + "FormDataEvent": false, + "FragmentDirective": false, + "frameElement": false, + "frames": false, + "GainNode": false, + "Gamepad": false, + "GamepadAxisMoveEvent": false, + "GamepadButton": false, + "GamepadButtonEvent": false, + "GamepadEvent": false, + "GamepadHapticActuator": false, + "GamepadPose": false, + "Geolocation": false, + "GeolocationCoordinates": false, + "GeolocationPosition": false, + "GeolocationPositionError": false, + "getComputedStyle": false, + "getScreenDetails": false, + "getSelection": false, + "GPU": false, + "GPUAdapter": false, + "GPUAdapterInfo": false, + "GPUBindGroup": false, + "GPUBindGroupLayout": false, + "GPUBuffer": false, + "GPUBufferUsage": false, + "GPUCanvasContext": false, + "GPUColorWrite": false, + "GPUCommandBuffer": false, + "GPUCommandEncoder": false, + "GPUCompilationInfo": false, + "GPUCompilationMessage": false, + "GPUComputePassEncoder": false, + "GPUComputePipeline": false, + "GPUDevice": false, + "GPUDeviceLostInfo": false, + "GPUError": false, + "GPUExternalTexture": false, + "GPUInternalError": false, + "GPUMapMode": false, + "GPUOutOfMemoryError": false, + "GPUPipelineError": false, + "GPUPipelineLayout": false, + "GPUQuerySet": false, + "GPUQueue": false, + "GPURenderBundle": false, + "GPURenderBundleEncoder": false, + "GPURenderPassEncoder": false, + "GPURenderPipeline": false, + "GPUSampler": false, + "GPUShaderModule": false, + "GPUShaderStage": false, + "GPUSupportedFeatures": false, + "GPUSupportedLimits": false, + "GPUTexture": false, + "GPUTextureUsage": false, + "GPUTextureView": false, + "GPUUncapturedErrorEvent": false, + "GPUValidationError": false, + "GravitySensor": false, + "Gyroscope": false, + "HashChangeEvent": false, + "Headers": false, + "HID": false, + "HIDConnectionEvent": false, + "HIDDevice": false, + "HIDInputReportEvent": false, + "Highlight": false, + "HighlightRegistry": false, + "history": false, + "History": false, + "HTMLAllCollection": false, + "HTMLAnchorElement": false, + "HTMLAreaElement": false, + "HTMLAudioElement": false, + "HTMLBaseElement": false, + "HTMLBodyElement": false, + "HTMLBRElement": false, + "HTMLButtonElement": false, + "HTMLCanvasElement": false, + "HTMLCollection": false, + "HTMLDataElement": false, + "HTMLDataListElement": false, + "HTMLDetailsElement": false, + "HTMLDialogElement": false, + "HTMLDirectoryElement": false, + "HTMLDivElement": false, + "HTMLDListElement": false, + "HTMLDocument": false, + "HTMLElement": false, + "HTMLEmbedElement": false, + "HTMLFencedFrameElement": false, + "HTMLFieldSetElement": false, + "HTMLFontElement": false, + "HTMLFormControlsCollection": false, + "HTMLFormElement": false, + "HTMLFrameElement": false, + "HTMLFrameSetElement": false, + "HTMLHeadElement": false, + "HTMLHeadingElement": false, + "HTMLHRElement": false, + "HTMLHtmlElement": false, + "HTMLIFrameElement": false, + "HTMLImageElement": false, + "HTMLInputElement": false, + "HTMLLabelElement": false, + "HTMLLegendElement": false, + "HTMLLIElement": false, + "HTMLLinkElement": false, + "HTMLMapElement": false, + "HTMLMarqueeElement": false, + "HTMLMediaElement": false, + "HTMLMenuElement": false, + "HTMLMetaElement": false, + "HTMLMeterElement": false, + "HTMLModElement": false, + "HTMLObjectElement": false, + "HTMLOListElement": false, + "HTMLOptGroupElement": false, + "HTMLOptionElement": false, + "HTMLOptionsCollection": false, + "HTMLOutputElement": false, + "HTMLParagraphElement": false, + "HTMLParamElement": false, + "HTMLPictureElement": false, + "HTMLPreElement": false, + "HTMLProgressElement": false, + "HTMLQuoteElement": false, + "HTMLScriptElement": false, + "HTMLSelectElement": false, + "HTMLSlotElement": false, + "HTMLSourceElement": false, + "HTMLSpanElement": false, + "HTMLStyleElement": false, + "HTMLTableCaptionElement": false, + "HTMLTableCellElement": false, + "HTMLTableColElement": false, + "HTMLTableElement": false, + "HTMLTableRowElement": false, + "HTMLTableSectionElement": false, + "HTMLTemplateElement": false, + "HTMLTextAreaElement": false, + "HTMLTimeElement": false, + "HTMLTitleElement": false, + "HTMLTrackElement": false, + "HTMLUListElement": false, + "HTMLUnknownElement": false, + "HTMLVideoElement": false, + "IDBCursor": false, + "IDBCursorWithValue": false, + "IDBDatabase": false, + "IDBFactory": false, + "IDBIndex": false, + "IDBKeyRange": false, + "IDBObjectStore": false, + "IDBOpenDBRequest": false, + "IDBRequest": false, + "IDBTransaction": false, + "IDBVersionChangeEvent": false, + "IdentityCredential": false, + "IdentityCredentialError": false, + "IdentityProvider": false, + "IdleDeadline": false, + "IdleDetector": false, + "IIRFilterNode": false, + "Image": false, + "ImageBitmap": false, + "ImageBitmapRenderingContext": false, + "ImageCapture": false, + "ImageData": false, + "ImageDecoder": false, + "ImageTrack": false, + "ImageTrackList": false, + "indexedDB": false, + "Ink": false, + "innerHeight": false, + "innerWidth": false, + "InputDeviceCapabilities": false, + "InputDeviceInfo": false, + "InputEvent": false, + "IntersectionObserver": false, + "IntersectionObserverEntry": false, + "isSecureContext": false, + "Keyboard": false, + "KeyboardEvent": false, + "KeyboardLayoutMap": false, + "KeyframeEffect": false, + "LargestContentfulPaint": false, + "LaunchParams": false, + "launchQueue": false, + "LaunchQueue": false, + "LayoutShift": false, + "LayoutShiftAttribution": false, + "length": false, + "LinearAccelerationSensor": false, + "localStorage": false, + "location": true, + "Location": false, + "locationbar": false, + "Lock": false, + "LockManager": false, + "matchMedia": false, + "MathMLElement": false, + "MediaCapabilities": false, + "MediaCapabilitiesInfo": false, + "MediaDeviceInfo": false, + "MediaDevices": false, + "MediaElementAudioSourceNode": false, + "MediaEncryptedEvent": false, + "MediaError": false, + "MediaKeyError": false, + "MediaKeyMessageEvent": false, + "MediaKeys": false, + "MediaKeySession": false, + "MediaKeyStatusMap": false, + "MediaKeySystemAccess": false, + "MediaList": false, + "MediaMetadata": false, + "MediaQueryList": false, + "MediaQueryListEvent": false, + "MediaRecorder": false, + "MediaRecorderErrorEvent": false, + "MediaSession": false, + "MediaSource": false, + "MediaSourceHandle": false, + "MediaStream": false, + "MediaStreamAudioDestinationNode": false, + "MediaStreamAudioSourceNode": false, + "MediaStreamEvent": false, + "MediaStreamTrack": false, + "MediaStreamTrackAudioSourceNode": false, + "MediaStreamTrackAudioStats": false, + "MediaStreamTrackEvent": false, + "MediaStreamTrackGenerator": false, + "MediaStreamTrackProcessor": false, + "MediaStreamTrackVideoStats": false, + "menubar": false, + "MessageChannel": false, + "MessageEvent": false, + "MessagePort": false, + "MIDIAccess": false, + "MIDIConnectionEvent": false, + "MIDIInput": false, + "MIDIInputMap": false, + "MIDIMessageEvent": false, + "MIDIOutput": false, + "MIDIOutputMap": false, + "MIDIPort": false, + "MimeType": false, + "MimeTypeArray": false, + "model": false, + "ModelGenericSession": false, + "ModelManager": false, + "MouseEvent": false, + "moveBy": false, + "moveTo": false, + "MutationEvent": false, + "MutationObserver": false, + "MutationRecord": false, + "name": false, + "NamedNodeMap": false, + "NavigateEvent": false, + "navigation": false, + "Navigation": false, + "NavigationActivation": false, + "NavigationCurrentEntryChangeEvent": false, + "NavigationDestination": false, + "NavigationHistoryEntry": false, + "NavigationPreloadManager": false, + "NavigationTransition": false, + "navigator": false, + "Navigator": false, + "NavigatorLogin": false, + "NavigatorManagedData": false, + "NavigatorUAData": false, + "NetworkInformation": false, + "Node": false, + "NodeFilter": false, + "NodeIterator": false, + "NodeList": false, + "Notification": false, + "NotifyPaintEvent": false, + "NotRestoredReasonDetails": false, + "NotRestoredReasons": false, + "OfflineAudioCompletionEvent": false, + "OfflineAudioContext": false, + "offscreenBuffering": false, + "OffscreenCanvas": false, + "OffscreenCanvasRenderingContext2D": false, + "onabort": true, + "onafterprint": true, + "onanimationcancel": true, + "onanimationend": true, + "onanimationiteration": true, + "onanimationstart": true, + "onappinstalled": true, + "onauxclick": true, + "onbeforeinput": true, + "onbeforeinstallprompt": true, + "onbeforematch": true, + "onbeforeprint": true, + "onbeforetoggle": true, + "onbeforeunload": true, + "onbeforexrselect": true, + "onblur": true, + "oncancel": true, + "oncanplay": true, + "oncanplaythrough": true, + "onchange": true, + "onclick": true, + "onclose": true, + "oncontentvisibilityautostatechange": true, + "oncontextlost": true, + "oncontextmenu": true, + "oncontextrestored": true, + "oncopy": true, + "oncuechange": true, + "oncut": true, + "ondblclick": true, + "ondevicemotion": true, + "ondeviceorientation": true, + "ondeviceorientationabsolute": true, + "ondrag": true, + "ondragend": true, + "ondragenter": true, + "ondragleave": true, + "ondragover": true, + "ondragstart": true, + "ondrop": true, + "ondurationchange": true, + "onemptied": true, + "onended": true, + "onerror": true, + "onfocus": true, + "onformdata": true, + "ongamepadconnected": true, + "ongamepaddisconnected": true, + "ongotpointercapture": true, + "onhashchange": true, + "oninput": true, + "oninvalid": true, + "onkeydown": true, + "onkeypress": true, + "onkeyup": true, + "onlanguagechange": true, + "onload": true, + "onloadeddata": true, + "onloadedmetadata": true, + "onloadstart": true, + "onlostpointercapture": true, + "onmessage": true, + "onmessageerror": true, + "onmousedown": true, + "onmouseenter": true, + "onmouseleave": true, + "onmousemove": true, + "onmouseout": true, + "onmouseover": true, + "onmouseup": true, + "onmousewheel": true, + "onoffline": true, + "ononline": true, + "onpagehide": true, + "onpagereveal": true, + "onpageshow": true, + "onpageswap": true, + "onpaste": true, + "onpause": true, + "onplay": true, + "onplaying": true, + "onpointercancel": true, + "onpointerdown": true, + "onpointerenter": true, + "onpointerleave": true, + "onpointermove": true, + "onpointerout": true, + "onpointerover": true, + "onpointerrawupdate": true, + "onpointerup": true, + "onpopstate": true, + "onprogress": true, + "onratechange": true, + "onrejectionhandled": true, + "onreset": true, + "onresize": true, + "onscroll": true, + "onscrollend": true, + "onscrollsnapchange": true, + "onscrollsnapchanging": true, + "onsearch": true, + "onsecuritypolicyviolation": true, + "onseeked": true, + "onseeking": true, + "onselect": true, + "onselectionchange": true, + "onselectstart": true, + "onslotchange": true, + "onstalled": true, + "onstorage": true, + "onsubmit": true, + "onsuspend": true, + "ontimeupdate": true, + "ontoggle": true, + "ontransitioncancel": true, + "ontransitionend": true, + "ontransitionrun": true, + "ontransitionstart": true, + "onunhandledrejection": true, + "onunload": true, + "onvolumechange": true, + "onwaiting": true, + "onwheel": true, + "open": false, + "opener": false, + "Option": false, + "OrientationSensor": false, + "origin": false, + "originAgentCluster": false, + "OscillatorNode": false, + "OTPCredential": false, + "outerHeight": false, + "outerWidth": false, + "OverconstrainedError": false, + "PageRevealEvent": false, + "PageSwapEvent": false, + "PageTransitionEvent": false, + "pageXOffset": false, + "pageYOffset": false, + "PannerNode": false, + "parent": false, + "PasswordCredential": false, + "Path2D": false, + "PaymentAddress": false, + "PaymentManager": false, + "PaymentMethodChangeEvent": false, + "PaymentRequest": false, + "PaymentRequestUpdateEvent": false, + "PaymentResponse": false, + "performance": false, + "Performance": false, + "PerformanceElementTiming": false, + "PerformanceEntry": false, + "PerformanceEventTiming": false, + "PerformanceLongAnimationFrameTiming": false, + "PerformanceLongTaskTiming": false, + "PerformanceMark": false, + "PerformanceMeasure": false, + "PerformanceNavigation": false, + "PerformanceNavigationTiming": false, + "PerformanceObserver": false, + "PerformanceObserverEntryList": false, + "PerformancePaintTiming": false, + "PerformanceResourceTiming": false, + "PerformanceScriptTiming": false, + "PerformanceServerTiming": false, + "PerformanceTiming": false, + "PeriodicSyncManager": false, + "PeriodicWave": false, + "Permissions": false, + "PermissionStatus": false, + "PERSISTENT": false, + "personalbar": false, + "PictureInPictureEvent": false, + "PictureInPictureWindow": false, + "Plugin": false, + "PluginArray": false, + "PointerEvent": false, + "PopStateEvent": false, + "postMessage": false, + "Presentation": false, + "PresentationAvailability": false, + "PresentationConnection": false, + "PresentationConnectionAvailableEvent": false, + "PresentationConnectionCloseEvent": false, + "PresentationConnectionList": false, + "PresentationReceiver": false, + "PresentationRequest": false, + "PressureObserver": false, + "PressureRecord": false, + "print": false, + "ProcessingInstruction": false, + "Profiler": false, + "ProgressEvent": false, + "PromiseRejectionEvent": false, + "prompt": false, + "ProtectedAudience": false, + "PublicKeyCredential": false, + "PushManager": false, + "PushSubscription": false, + "PushSubscriptionOptions": false, + "queryLocalFonts": false, + "queueMicrotask": false, + "RadioNodeList": false, + "Range": false, + "ReadableByteStreamController": false, + "ReadableStream": false, + "ReadableStreamBYOBReader": false, + "ReadableStreamBYOBRequest": false, + "ReadableStreamDefaultController": false, + "ReadableStreamDefaultReader": false, + "registerProcessor": false, + "RelativeOrientationSensor": false, + "RemotePlayback": false, + "removeEventListener": false, + "reportError": false, + "ReportingObserver": false, + "Request": false, + "requestAnimationFrame": false, + "requestIdleCallback": false, + "resizeBy": false, + "ResizeObserver": false, + "ResizeObserverEntry": false, + "ResizeObserverSize": false, + "resizeTo": false, + "Response": false, + "RTCCertificate": false, + "RTCDataChannel": false, + "RTCDataChannelEvent": false, + "RTCDtlsTransport": false, + "RTCDTMFSender": false, + "RTCDTMFToneChangeEvent": false, + "RTCEncodedAudioFrame": false, + "RTCEncodedVideoFrame": false, + "RTCError": false, + "RTCErrorEvent": false, + "RTCIceCandidate": false, + "RTCIceTransport": false, + "RTCPeerConnection": false, + "RTCPeerConnectionIceErrorEvent": false, + "RTCPeerConnectionIceEvent": false, + "RTCRtpReceiver": false, + "RTCRtpScriptTransform": false, + "RTCRtpSender": false, + "RTCRtpTransceiver": false, + "RTCSctpTransport": false, + "RTCSessionDescription": false, + "RTCStatsReport": false, + "RTCTrackEvent": false, + "sampleRate": false, + "scheduler": false, + "Scheduler": false, + "Scheduling": false, + "screen": false, + "Screen": false, + "ScreenDetailed": false, + "ScreenDetails": false, + "screenLeft": false, + "ScreenOrientation": false, + "screenTop": false, + "screenX": false, + "screenY": false, + "ScriptProcessorNode": false, + "scroll": false, + "scrollbars": false, + "scrollBy": false, + "ScrollTimeline": false, + "scrollTo": false, + "scrollX": false, + "scrollY": false, + "SecurityPolicyViolationEvent": false, + "Selection": false, + "self": false, + "Sensor": false, + "SensorErrorEvent": false, + "Serial": false, + "SerialPort": false, + "ServiceWorker": false, + "ServiceWorkerContainer": false, + "ServiceWorkerRegistration": false, + "sessionStorage": false, + "setInterval": false, + "setTimeout": false, + "ShadowRoot": false, + "sharedStorage": false, + "SharedStorage": false, + "SharedStorageWorklet": false, + "SharedWorker": false, + "showDirectoryPicker": false, + "showOpenFilePicker": false, + "showSaveFilePicker": false, + "SnapEvent": false, + "SourceBuffer": false, + "SourceBufferList": false, + "speechSynthesis": false, + "SpeechSynthesis": false, + "SpeechSynthesisErrorEvent": false, + "SpeechSynthesisEvent": false, + "SpeechSynthesisUtterance": false, + "SpeechSynthesisVoice": false, + "StaticRange": false, + "status": false, + "statusbar": false, + "StereoPannerNode": false, + "stop": false, + "Storage": false, + "StorageBucket": false, + "StorageBucketManager": false, + "StorageEvent": false, + "StorageManager": false, + "structuredClone": false, + "styleMedia": false, + "StylePropertyMap": false, + "StylePropertyMapReadOnly": false, + "StyleSheet": false, + "StyleSheetList": false, + "SubmitEvent": false, + "SubtleCrypto": false, + "SVGAElement": false, + "SVGAngle": false, + "SVGAnimatedAngle": false, + "SVGAnimatedBoolean": false, + "SVGAnimatedEnumeration": false, + "SVGAnimatedInteger": false, + "SVGAnimatedLength": false, + "SVGAnimatedLengthList": false, + "SVGAnimatedNumber": false, + "SVGAnimatedNumberList": false, + "SVGAnimatedPreserveAspectRatio": false, + "SVGAnimatedRect": false, + "SVGAnimatedString": false, + "SVGAnimatedTransformList": false, + "SVGAnimateElement": false, + "SVGAnimateMotionElement": false, + "SVGAnimateTransformElement": false, + "SVGAnimationElement": false, + "SVGCircleElement": false, + "SVGClipPathElement": false, + "SVGComponentTransferFunctionElement": false, + "SVGDefsElement": false, + "SVGDescElement": false, + "SVGElement": false, + "SVGEllipseElement": false, + "SVGFEBlendElement": false, + "SVGFEColorMatrixElement": false, + "SVGFEComponentTransferElement": false, + "SVGFECompositeElement": false, + "SVGFEConvolveMatrixElement": false, + "SVGFEDiffuseLightingElement": false, + "SVGFEDisplacementMapElement": false, + "SVGFEDistantLightElement": false, + "SVGFEDropShadowElement": false, + "SVGFEFloodElement": false, + "SVGFEFuncAElement": false, + "SVGFEFuncBElement": false, + "SVGFEFuncGElement": false, + "SVGFEFuncRElement": false, + "SVGFEGaussianBlurElement": false, + "SVGFEImageElement": false, + "SVGFEMergeElement": false, + "SVGFEMergeNodeElement": false, + "SVGFEMorphologyElement": false, + "SVGFEOffsetElement": false, + "SVGFEPointLightElement": false, + "SVGFESpecularLightingElement": false, + "SVGFESpotLightElement": false, + "SVGFETileElement": false, + "SVGFETurbulenceElement": false, + "SVGFilterElement": false, + "SVGForeignObjectElement": false, + "SVGGElement": false, + "SVGGeometryElement": false, + "SVGGradientElement": false, + "SVGGraphicsElement": false, + "SVGImageElement": false, + "SVGLength": false, + "SVGLengthList": false, + "SVGLinearGradientElement": false, + "SVGLineElement": false, + "SVGMarkerElement": false, + "SVGMaskElement": false, + "SVGMatrix": false, + "SVGMetadataElement": false, + "SVGMPathElement": false, + "SVGNumber": false, + "SVGNumberList": false, + "SVGPathElement": false, + "SVGPatternElement": false, + "SVGPoint": false, + "SVGPointList": false, + "SVGPolygonElement": false, + "SVGPolylineElement": false, + "SVGPreserveAspectRatio": false, + "SVGRadialGradientElement": false, + "SVGRect": false, + "SVGRectElement": false, + "SVGScriptElement": false, + "SVGSetElement": false, + "SVGStopElement": false, + "SVGStringList": false, + "SVGStyleElement": false, + "SVGSVGElement": false, + "SVGSwitchElement": false, + "SVGSymbolElement": false, + "SVGTextContentElement": false, + "SVGTextElement": false, + "SVGTextPathElement": false, + "SVGTextPositioningElement": false, + "SVGTitleElement": false, + "SVGTransform": false, + "SVGTransformList": false, + "SVGTSpanElement": false, + "SVGUnitTypes": false, + "SVGUseElement": false, + "SVGViewElement": false, + "SyncManager": false, + "TaskAttributionTiming": false, + "TaskController": false, + "TaskPriorityChangeEvent": false, + "TaskSignal": false, + "TEMPORARY": false, + "Text": false, + "TextDecoder": false, + "TextDecoderStream": false, + "TextEncoder": false, + "TextEncoderStream": false, + "TextEvent": false, + "TextFormat": false, + "TextFormatUpdateEvent": false, + "TextMetrics": false, + "TextTrack": false, + "TextTrackCue": false, + "TextTrackCueList": false, + "TextTrackList": false, + "TextUpdateEvent": false, + "TimeEvent": false, + "TimeRanges": false, + "ToggleEvent": false, + "toolbar": false, + "top": false, + "Touch": false, + "TouchEvent": false, + "TouchList": false, + "TrackEvent": false, + "TransformStream": false, + "TransformStreamDefaultController": false, + "TransitionEvent": false, + "TreeWalker": false, + "TrustedHTML": false, + "TrustedScript": false, + "TrustedScriptURL": false, + "TrustedTypePolicy": false, + "TrustedTypePolicyFactory": false, + "trustedTypes": false, + "UIEvent": false, + "URL": false, + "URLPattern": false, + "URLSearchParams": false, + "USB": false, + "USBAlternateInterface": false, + "USBConfiguration": false, + "USBConnectionEvent": false, + "USBDevice": false, + "USBEndpoint": false, + "USBInterface": false, + "USBInTransferResult": false, + "USBIsochronousInTransferPacket": false, + "USBIsochronousInTransferResult": false, + "USBIsochronousOutTransferPacket": false, + "USBIsochronousOutTransferResult": false, + "USBOutTransferResult": false, + "UserActivation": false, + "ValidityState": false, + "VideoColorSpace": false, + "VideoDecoder": false, + "VideoEncoder": false, + "VideoFrame": false, + "VideoPlaybackQuality": false, + "ViewTimeline": false, + "ViewTransition": false, + "ViewTransitionTypeSet": false, + "VirtualKeyboard": false, + "VirtualKeyboardGeometryChangeEvent": false, + "VisibilityStateEntry": false, + "visualViewport": false, + "VisualViewport": false, + "VTTCue": false, + "VTTRegion": false, + "WakeLock": false, + "WakeLockSentinel": false, + "WaveShaperNode": false, + "WebAssembly": false, + "WebGL2RenderingContext": false, + "WebGLActiveInfo": false, + "WebGLBuffer": false, + "WebGLContextEvent": false, + "WebGLFramebuffer": false, + "WebGLObject": false, + "WebGLProgram": false, + "WebGLQuery": false, + "WebGLRenderbuffer": false, + "WebGLRenderingContext": false, + "WebGLSampler": false, + "WebGLShader": false, + "WebGLShaderPrecisionFormat": false, + "WebGLSync": false, + "WebGLTexture": false, + "WebGLTransformFeedback": false, + "WebGLUniformLocation": false, + "WebGLVertexArrayObject": false, + "WebSocket": false, + "WebSocketError": false, + "WebSocketStream": false, + "WebTransport": false, + "WebTransportBidirectionalStream": false, + "WebTransportDatagramDuplexStream": false, + "WebTransportError": false, + "WebTransportReceiveStream": false, + "WebTransportSendStream": false, + "WGSLLanguageFeatures": false, + "WheelEvent": false, + "window": false, + "Window": false, + "WindowControlsOverlay": false, + "WindowControlsOverlayGeometryChangeEvent": false, + "Worker": false, + "Worklet": false, + "WorkletGlobalScope": false, + "WritableStream": false, + "WritableStreamDefaultController": false, + "WritableStreamDefaultWriter": false, + "XMLDocument": false, + "XMLHttpRequest": false, + "XMLHttpRequestEventTarget": false, + "XMLHttpRequestUpload": false, + "XMLSerializer": false, + "XPathEvaluator": false, + "XPathExpression": false, + "XPathResult": false, + "XRAnchor": false, + "XRAnchorSet": false, + "XRBoundedReferenceSpace": false, + "XRCamera": false, + "XRCPUDepthInformation": false, + "XRDepthInformation": false, + "XRDOMOverlayState": false, + "XRFrame": false, + "XRHand": false, + "XRHitTestResult": false, + "XRHitTestSource": false, + "XRInputSource": false, + "XRInputSourceArray": false, + "XRInputSourceEvent": false, + "XRInputSourcesChangeEvent": false, + "XRJointPose": false, + "XRJointSpace": false, + "XRLayer": false, + "XRLightEstimate": false, + "XRLightProbe": false, + "XRPose": false, + "XRRay": false, + "XRReferenceSpace": false, + "XRReferenceSpaceEvent": false, + "XRRenderState": false, + "XRRigidTransform": false, + "XRSession": false, + "XRSessionEvent": false, + "XRSpace": false, + "XRSystem": false, + "XRTransientInputHitTestResult": false, + "XRTransientInputHitTestSource": false, + "XRView": false, + "XRViewerPose": false, + "XRViewport": false, + "XRWebGLBinding": false, + "XRWebGLDepthInformation": false, + "XRWebGLLayer": false, + "XSLTProcessor": false, + "__dirname": false, + "__filename": false, + "Buffer": false, + "clearImmediate": false, + "exports": true, + "global": false, + "module": false, + "process": false, + "require": false, + "setImmediate": false + } + } +} diff --git a/03_source/frontend/package.json b/03_source/frontend/package.json index dd020e7..ee5ebb8 100644 --- a/03_source/frontend/package.json +++ b/03_source/frontend/package.json @@ -105,6 +105,7 @@ "react-organizational-chart": "^2.2.1", "react-phone-number-input": "^3.4.12", "react-router": "^7.4.1", + "react-use": "^17.6.0", "rehype-highlight": "^7.0.2", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", diff --git a/03_source/frontend/src/actions/invoice.ts b/03_source/frontend/src/actions/invoice.ts index d601f92..a157543 100644 --- a/03_source/frontend/src/actions/invoice.ts +++ b/03_source/frontend/src/actions/invoice.ts @@ -1,7 +1,7 @@ // src/actions/invoice.ts import { useMemo } from 'react'; import axiosInstance, { endpoints, fetcher } from 'src/lib/axios'; -import type { IInvoiceItem } from 'src/types/invoice'; +import type { IInvoiceItem, SaveInvoiceData } from 'src/types/invoice'; import type { SWRConfiguration } from 'swr'; import useSWR from 'swr'; @@ -97,7 +97,7 @@ export function useSearchInvoices(query: string) { // ---------------------------------------------------------------------- -type SaveInvoiceData = IInvoiceItem; +// type SaveInvoiceData = IInvoiceItem; export async function saveInvoice(invoiceId: string, saveInvoiceData: SaveInvoiceData) { const url = endpoints.invoice.saveInvoice(invoiceId); diff --git a/03_source/frontend/src/actions/product.ts b/03_source/frontend/src/actions/product.ts index 0e4e3a5..9851593 100644 --- a/03_source/frontend/src/actions/product.ts +++ b/03_source/frontend/src/actions/product.ts @@ -99,6 +99,7 @@ export function useSearchProducts(query: string) { type SaveProductData = { // id: string; + sku: string; name: string; code: string; diff --git a/03_source/frontend/src/auth/context/jwt/action.ts b/03_source/frontend/src/auth/context/jwt/action.ts index 8b6d77b..7a8491e 100644 --- a/03_source/frontend/src/auth/context/jwt/action.ts +++ b/03_source/frontend/src/auth/context/jwt/action.ts @@ -17,6 +17,8 @@ export type SignUpParams = { lastName: string; }; +const ERR_ACCESS_TOKEN_NOT_FOUND = `Access token not found in response`; + /** ************************************** * Sign in *************************************** */ @@ -29,7 +31,7 @@ export const signInWithPassword = async ({ email, password }: SignInParams): Pro const { accessToken } = res.data; if (!accessToken) { - throw new Error('Access token not found in response'); + throw new Error(ERR_ACCESS_TOKEN_NOT_FOUND); } setSession(accessToken); diff --git a/03_source/frontend/src/auth/context/jwt/utils.ts b/03_source/frontend/src/auth/context/jwt/utils.ts index 9c154c0..9c71ea5 100644 --- a/03_source/frontend/src/auth/context/jwt/utils.ts +++ b/03_source/frontend/src/auth/context/jwt/utils.ts @@ -69,6 +69,8 @@ export function tokenExpired(exp: number) { // ---------------------------------------------------------------------- +const INVALID_ACCESS_TOKEN = 'Invalid access token!'; + export async function setSession(accessToken: string | null) { try { if (accessToken) { @@ -81,7 +83,7 @@ export async function setSession(accessToken: string | null) { if (decodedToken && 'exp' in decodedToken) { tokenExpired(decodedToken.exp); } else { - throw new Error('Invalid access token!'); + throw new Error(INVALID_ACCESS_TOKEN); } } else { sessionStorage.removeItem(JWT_STORAGE_KEY); diff --git a/03_source/frontend/src/pages/dashboard/user/edit.tsx b/03_source/frontend/src/pages/dashboard/user/edit.tsx index a757b1b..ef99434 100644 --- a/03_source/frontend/src/pages/dashboard/user/edit.tsx +++ b/03_source/frontend/src/pages/dashboard/user/edit.tsx @@ -11,7 +11,7 @@ const metadata = { title: `User edit | Dashboard - ${CONFIG.appName}` }; export default function Page() { const { id = '' } = useParams(); - // TODO: remove me + // TODO: remove unused code // const currentUser = _userList.find((user) => user.id === id); const { user } = useGetUser(id); diff --git a/03_source/frontend/src/sections/calendar/calendar-toolbar.tsx b/03_source/frontend/src/sections/calendar/calendar-toolbar.tsx index 4f8b37d..42b412d 100644 --- a/03_source/frontend/src/sections/calendar/calendar-toolbar.tsx +++ b/03_source/frontend/src/sections/calendar/calendar-toolbar.tsx @@ -106,7 +106,8 @@ export function CalendarToolbar({ - {date} + {/* FIXME: no raw json output in html */} + {JSON.stringify({ date })} diff --git a/03_source/frontend/src/sections/invoice/invoice-details.tsx b/03_source/frontend/src/sections/invoice/invoice-details.tsx index 0d3ec8b..a725dc4 100644 --- a/03_source/frontend/src/sections/invoice/invoice-details.tsx +++ b/03_source/frontend/src/sections/invoice/invoice-details.tsx @@ -37,19 +37,22 @@ export function InvoiceDetails({ invoice }: Props) { const { t } = useTranslation(); const [currentStatus, setCurrentStatus] = useState(invoice?.status); - const handleChangeStatus = useCallback((event: React.ChangeEvent) => { - // setCurrentStatus(event.target.value); + const handleChangeStatus = useCallback( + (event: React.ChangeEvent) => { + // setCurrentStatus(event.target.value); - try { - changeStatus(invoice.id, event.target.value); - setCurrentStatus(event.target.value); + try { + changeStatus(invoice.id, event.target.value); + setCurrentStatus(event.target.value); - toast.success('status changed!'); - } catch (error) { - console.error(error); - toast.warning('error during changing status'); - } - }, []); + toast.success('status changed!'); + } catch (error) { + console.error(error); + toast.warning('error during changing status'); + } + }, + [] + ); const renderFooter = () => ( = { +export const defaultItem: Omit = { title: '', description: '', service: INVOICE_SERVICE_OPTIONS[0].name, @@ -50,7 +50,7 @@ export function InvoiceNewEditDetails() { const discount = getValues('discount'); const shipping = getValues('shipping'); - const subtotal = sumBy(items, (item: IInvoiceItem) => item.quantity * item.price); + const subtotal = sumBy(items, (item: IInvoiceItemItem) => item.quantity * item.price); const subtotalWithTax = subtotal + subtotal * (taxes / 100); const totalAmount = subtotalWithTax - discount - shipping; diff --git a/03_source/frontend/src/sections/invoice/invoice-new-edit-form.tsx b/03_source/frontend/src/sections/invoice/invoice-new-edit-form.tsx index 3b0c4f9..b0caf55 100644 --- a/03_source/frontend/src/sections/invoice/invoice-new-edit-form.tsx +++ b/03_source/frontend/src/sections/invoice/invoice-new-edit-form.tsx @@ -81,6 +81,8 @@ export function InvoiceNewEditForm({ currentInvoice }: Props) { const loadingSend = useBoolean(); const defaultValues: NewInvoiceSchemaType = { + id: '', + sent: 0, invoiceNumber: 'INV-1990', createDate: today(), dueDate: null, @@ -129,6 +131,8 @@ export function InvoiceNewEditForm({ currentInvoice }: Props) { try { if (currentInvoice) { + data.dueDate = '2029-01-01'; + await saveInvoice(currentInvoice.id, data); } diff --git a/03_source/frontend/src/sections/invoice/invoice-toolbar.tsx b/03_source/frontend/src/sections/invoice/invoice-toolbar.tsx index 0a41677..6b87f02 100644 --- a/03_source/frontend/src/sections/invoice/invoice-toolbar.tsx +++ b/03_source/frontend/src/sections/invoice/invoice-toolbar.tsx @@ -27,7 +27,7 @@ type Props = { invoice?: IInvoiceItem; currentStatus: string; statusOptions: { value: string; label: string }[]; - onChangeStatus: (event: React.ChangeEvent) => void; + onChangeStatus: (event: React.ChangeEvent) => void; }; export function InvoiceToolbar({ invoice, currentStatus, statusOptions, onChangeStatus }: Props) { diff --git a/03_source/frontend/src/types/common.ts b/03_source/frontend/src/types/common.ts index a94c526..91a40d2 100644 --- a/03_source/frontend/src/types/common.ts +++ b/03_source/frontend/src/types/common.ts @@ -19,7 +19,7 @@ export type IAddressItem = { addressType?: string; }; -export type IDateValue = string | number | null; +export type IDateValue = string | number | Date | null; export type IDatePickerControl = Dayjs | null; diff --git a/03_source/frontend/src/types/invoice.ts b/03_source/frontend/src/types/invoice.ts index 0abd6db..fc0acc3 100644 --- a/03_source/frontend/src/types/invoice.ts +++ b/03_source/frontend/src/types/invoice.ts @@ -13,15 +13,16 @@ export type IInvoiceTableFilters = { export type IInvoiceItemItem = { id: string; title: string; + service: string; price: number; total: number; - service: string; quantity: number; description: string; }; export type IInvoiceItem = { id: string; + sent: number; taxes: number; status: string; @@ -36,3 +37,26 @@ export type IInvoiceItem = { invoiceTo: IAddressItem; invoiceFrom: IAddressItem; }; + +export type SaveInvoiceData = { + sent: number; + taxes: number; + status: string; + subtotal: number; + discount: number; + shipping: number; + totalAmount: number; + dueDate: IDateValue; + invoiceNumber: string; + items: { + title: string; + service: string; + price: number; + total: number; + quantity: number; + description: string; + }[]; + createDate: IDateValue; + invoiceTo: IAddressItem | null; + invoiceFrom: IAddressItem | null; +}; diff --git a/03_source/frontend/yarn.lock b/03_source/frontend/yarn.lock index ac10b81..767a9ff 100644 --- a/03_source/frontend/yarn.lock +++ b/03_source/frontend/yarn.lock @@ -766,6 +766,11 @@ dependencies: "@babel/types" "^7.27.0" +"@babel/runtime@^7.1.2": + version "7.27.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.4.tgz#a91ec580e6c00c67118127777c316dfd5a5a6abf" + integrity sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA== + "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.13", "@babel/runtime@^7.23.2", "@babel/runtime@^7.25.0", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.26.10", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": version "7.27.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz" @@ -1753,7 +1758,7 @@ resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": version "1.5.0" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== @@ -3298,6 +3303,11 @@ dependencies: "@types/unist" "*" +"@types/js-cookie@^2.2.6": + version "2.2.7" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3" + integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA== + "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" @@ -3627,6 +3637,11 @@ dependencies: "@swc/core" "^1.11.11" +"@xobotyi/scrollbar-width@^1.9.5": + version "1.9.5" + resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" + integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== + "@yr/monotone-cubic-spline@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz" @@ -4090,6 +4105,13 @@ cookie@^1.0.1: resolved "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz" integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== +copy-to-clipboard@^3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== + dependencies: + toggle-selection "^1.0.6" + cosmiconfig@^7.0.0: version "7.1.0" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz" @@ -4130,6 +4152,21 @@ crypto-js@^4.2.0: resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz" integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== +css-in-js-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb" + integrity sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A== + dependencies: + hyphenate-style-name "^1.0.3" + +css-tree@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + csscolorparser@~1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz" @@ -4140,7 +4177,7 @@ cssjanus@^2.0.1: resolved "https://registry.npmjs.org/cssjanus/-/cssjanus-2.3.0.tgz" integrity sha512-ZZXXn51SnxRxAZ6fdY7mBDPmA4OZd83q/J9Gdqz3YmE9TUq+9tZl+tdOnCi7PpNygI6PEkehj9rgifv5+W8a5A== -csstype@^3.0.2, csstype@^3.1.3: +csstype@^3.0.2, csstype@^3.1.2, csstype@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== @@ -4332,6 +4369,13 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9: version "1.23.9" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz" @@ -4749,6 +4793,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== + fast-xml-parser@4.4.1: version "4.4.1" resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz" @@ -4763,6 +4812,11 @@ fast-xml-parser@^4.4.1: dependencies: strnum "^1.1.1" +fastest-stable-stringify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" + integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q== + fastq@^1.6.0: version "1.19.1" resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz" @@ -5256,6 +5310,11 @@ hyphen@^1.6.4: resolved "https://registry.npmjs.org/hyphen/-/hyphen-1.10.6.tgz" integrity sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw== +hyphenate-style-name@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz#1797bf50369588b47b72ca6d5e65374607cf4436" + integrity sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw== + i18next-browser-languagedetector@^8.0.4: version "8.0.4" resolved "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.4.tgz" @@ -5325,6 +5384,13 @@ inline-style-parser@0.2.4: resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz" integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== +inline-style-prefixer@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz#9310f3cfa2c6f3901d1480f373981c02691781e8" + integrity sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw== + dependencies: + css-in-js-utils "^3.1.0" + input-format@^0.3.10: version "0.3.14" resolved "https://registry.npmjs.org/input-format/-/input-format-0.3.14.tgz" @@ -5631,6 +5697,11 @@ jay-peg@^1.1.1: dependencies: restructure "^3.0.0" +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + js-cookie@^3.0.5: version "3.0.5" resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz" @@ -6029,6 +6100,11 @@ mdast-util-to-string@^4.0.0: dependencies: "@types/mdast" "^4.0.0" +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + mdurl@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz" @@ -6397,6 +6473,20 @@ murmurhash-js@^1.0.0: resolved "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz" integrity sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw== +nano-css@^5.6.2: + version "5.6.2" + resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.6.2.tgz#584884ddd7547278f6d6915b6805069742679a32" + integrity sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + css-tree "^1.1.2" + csstype "^3.1.2" + fastest-stable-stringify "^2.0.2" + inline-style-prefixer "^7.0.1" + rtl-css-js "^1.16.1" + stacktrace-js "^2.0.2" + stylis "^4.3.0" + nanoid@^3.3.8: version "3.3.11" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" @@ -7014,6 +7104,31 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" +react-universal-interface@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" + integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== + +react-use@^17.6.0: + version "17.6.0" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.6.0.tgz#2101a3a79dc965a25866b21f5d6de4b128488a14" + integrity sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g== + dependencies: + "@types/js-cookie" "^2.2.6" + "@xobotyi/scrollbar-width" "^1.9.5" + copy-to-clipboard "^3.3.1" + fast-deep-equal "^3.1.3" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.6.2" + react-universal-interface "^0.6.2" + resize-observer-polyfill "^1.5.1" + screenfull "^5.1.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^3.0.1" + ts-easing "^0.2.0" + tslib "^2.1.0" + react@^19.0.0, react@^19.1.0: version "19.1.0" resolved "https://registry.npmjs.org/react/-/react-19.1.0.tgz" @@ -7137,6 +7252,11 @@ reselect@^5.1.1: resolved "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz" integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" @@ -7216,6 +7336,13 @@ rope-sequence@^1.3.0: resolved "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz" integrity sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ== +rtl-css-js@^1.16.1: + version "1.16.1" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.16.1.tgz#4b48b4354b0ff917a30488d95100fbf7219a3e80" + integrity sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg== + dependencies: + "@babel/runtime" "^7.1.2" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" @@ -7278,6 +7405,11 @@ scheduler@^0.26.0: resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz" integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== +screenfull@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" + integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== + semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" @@ -7325,6 +7457,11 @@ set-function-name@^2.0.2: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + set-proto@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz" @@ -7449,11 +7586,21 @@ source-map-js@^1.2.1: resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA== + source-map@^0.5.7: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + space-separated-tokens@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz" @@ -7471,6 +7618,35 @@ stable-hash@^0.0.5: resolved "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz" integrity sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA== +stack-generator@^2.0.5: + version "2.0.10" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d" + integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ== + dependencies: + stackframe "^1.3.4" + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +stacktrace-gps@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0" + integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ== + dependencies: + source-map "0.5.6" + stackframe "^1.3.4" + +stacktrace-js@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" + integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== + dependencies: + error-stack-parser "^2.0.6" + stack-generator "^2.0.5" + stacktrace-gps "^3.0.4" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" @@ -7609,7 +7785,7 @@ stylis@4.2.0: resolved "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz" integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== -stylis@^4.3.6: +stylis@^4.3.0, stylis@^4.3.6: version "4.3.6" resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz" integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== @@ -7646,6 +7822,11 @@ swr@^2.3.3: dequal "^2.0.3" use-sync-external-store "^1.4.0" +throttle-debounce@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" + integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== + tiny-inflate@^1.0.0, tiny-inflate@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz" @@ -7683,6 +7864,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" @@ -7703,6 +7889,11 @@ ts-api-utils@^2.0.1: resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz" integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" diff --git a/03_source/mobile/.prettierrc b/03_source/mobile/.prettierrc index cc1c65c..cbd1fe3 100644 --- a/03_source/mobile/.prettierrc +++ b/03_source/mobile/.prettierrc @@ -2,5 +2,5 @@ "semi": true, "singleQuote": true, "trailingComma": "es5", - "printWidth": 240 + "printWidth": 100 } diff --git a/03_source/mobile/package-lock.json b/03_source/mobile/package-lock.json index bb0f073..b208ffd 100644 --- a/03_source/mobile/package-lock.json +++ b/03_source/mobile/package-lock.json @@ -13,6 +13,7 @@ "@capacitor/core": "^7.0.0", "@capacitor/ios": "7.0.1", "@capacitor/preferences": "^7.0.0", + "@hookform/resolvers": "^4.1.3", "@ionic/react": "^8.5.0", "@ionic/react-router": "^8.5.0", "@mdx-js/react": "^3.1.0", @@ -24,15 +25,18 @@ "leaflet": "^1.9.4", "react": "19.0.0", "react-dom": "19.0.0", + "react-hook-form": "^7.55.0", "react-leaflet": "^5.0.0", "react-markdown": "^10.1.0", "react-redux": "^9.2.0", "react-router": "^5.3.4", "react-router-dom": "^5.3.4", "react-spinners": "^0.17.0", + "react-use": "^17.6.0", "reselect": "^4.0.0", "sass": "^1.85.1", - "swiper": "^9.1.1" + "swiper": "^9.1.1", + "zod": "^3.24.2" }, "devDependencies": { "@capacitor/cli": "^7.0.0", @@ -871,6 +875,18 @@ "node": ">=18" } }, + "node_modules/@hookform/resolvers": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-4.1.3.tgz", + "integrity": "sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@ionic/cli-framework-output": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz", @@ -1233,7 +1249,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -1865,6 +1880,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@stencil/core": { "version": "4.20.0", "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.20.0.tgz", @@ -2072,6 +2093,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/js-cookie": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz", + "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==", + "license": "MIT" + }, "node_modules/@types/leaflet": { "version": "1.9.18", "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.18.tgz", @@ -2351,6 +2378,12 @@ "node": ">=10.0.0" } }, + "node_modules/@xobotyi/scrollbar-width": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", + "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==", + "license": "MIT" + }, "node_modules/ansi-escapes": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", @@ -2889,6 +2922,15 @@ "dev": true, "license": "MIT" }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-js-pure": { "version": "3.42.0", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.42.0.tgz", @@ -2916,6 +2958,28 @@ "node": ">= 8" } }, + "node_modules/css-in-js-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", + "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==", + "license": "MIT", + "dependencies": { + "hyphenate-style-name": "^1.0.3" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3087,6 +3151,15 @@ "node": ">=6" } }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -3230,6 +3303,23 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz", + "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==" + }, + "node_modules/fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==", + "license": "MIT" + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -3635,6 +3725,12 @@ "node": ">=14.18.0" } }, + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", + "license": "BSD-3-Clause" + }, "node_modules/immutable": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz", @@ -3664,6 +3760,15 @@ "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", "license": "MIT" }, + "node_modules/inline-style-prefixer": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz", + "integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==", + "license": "MIT", + "dependencies": { + "css-in-js-utils": "^3.1.0" + } + }, "node_modules/ionicons": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.4.0.tgz", @@ -3843,6 +3948,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4427,6 +4538,12 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "license": "CC0-1.0" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5003,6 +5120,26 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/nano-css": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.2.tgz", + "integrity": "sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==", + "license": "Unlicense", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "css-tree": "^1.1.2", + "csstype": "^3.1.2", + "fastest-stable-stringify": "^2.0.2", + "inline-style-prefixer": "^7.0.1", + "rtl-css-js": "^1.16.1", + "stacktrace-js": "^2.0.2", + "stylis": "^4.3.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -5400,6 +5537,22 @@ "react": "^19.0.0" } }, + "node_modules/react-hook-form": { + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.57.0.tgz", + "integrity": "sha512-RbEks3+cbvTP84l/VXGUZ+JMrKOS8ykQCRYdm5aYsxnDquL0vspsyNhGRO7pcH6hsZqWlPOjLye7rJqdtdAmlg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -5528,6 +5681,41 @@ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-universal-interface": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "peerDependencies": { + "react": "*", + "tslib": "*" + } + }, + "node_modules/react-use": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.6.0.tgz", + "integrity": "sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g==", + "license": "Unlicense", + "dependencies": { + "@types/js-cookie": "^2.2.6", + "@xobotyi/scrollbar-width": "^1.9.5", + "copy-to-clipboard": "^3.3.1", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.6.2", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.1.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^3.0.1", + "ts-easing": "^0.2.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -5603,6 +5791,12 @@ "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", "license": "MIT" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, "node_modules/resolve-pathname": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", @@ -5719,6 +5913,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/rtl-css-js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz", + "integrity": "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5773,6 +5976,18 @@ "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", "license": "MIT" }, + "node_modules/screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", @@ -5786,6 +6001,15 @@ "node": ">=10" } }, + "node_modules/set-harmonic-interval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz", + "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==", + "license": "Unlicense", + "engines": { + "node": ">=6.9" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5841,6 +6065,15 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5876,6 +6109,51 @@ "integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ==", "license": "MIT" }, + "node_modules/stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" + }, + "node_modules/stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "license": "MIT", + "dependencies": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + } + }, + "node_modules/stacktrace-gps/node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "license": "MIT", + "dependencies": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -5999,6 +6277,12 @@ "inline-style-parser": "0.2.4" } }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6069,6 +6353,15 @@ "dev": true, "license": "ISC" }, + "node_modules/throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/through2": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", @@ -6149,6 +6442,12 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -6179,6 +6478,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-easing": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", + "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==", + "license": "Unlicense" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -6637,6 +6942,15 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/zod": { + "version": "3.25.46", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.46.tgz", + "integrity": "sha512-IqRxcHEIjqLd4LNS/zKffB3Jzg3NwqJxQQ0Ns7pdrvgGkwQsEBdEQcOHaBVqvvZArShRzI39+aMST3FBGmTrLQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/03_source/mobile/package.json b/03_source/mobile/package.json index 1888af3..4b631e9 100644 --- a/03_source/mobile/package.json +++ b/03_source/mobile/package.json @@ -10,6 +10,7 @@ "@capacitor/core": "^7.0.0", "@capacitor/ios": "7.0.1", "@capacitor/preferences": "^7.0.0", + "@hookform/resolvers": "^4.1.3", "@ionic/react": "^8.5.0", "@ionic/react-router": "^8.5.0", "@mdx-js/react": "^3.1.0", @@ -21,15 +22,18 @@ "leaflet": "^1.9.4", "react": "19.0.0", "react-dom": "19.0.0", + "react-hook-form": "^7.55.0", "react-leaflet": "^5.0.0", "react-markdown": "^10.1.0", "react-redux": "^9.2.0", "react-router": "^5.3.4", "react-router-dom": "^5.3.4", "react-spinners": "^0.17.0", + "react-use": "^17.6.0", "reselect": "^4.0.0", "sass": "^1.85.1", - "swiper": "^9.1.1" + "swiper": "^9.1.1", + "zod": "^3.24.2" }, "scripts": { "start": "npm run dev", diff --git a/03_source/mobile/src/App.tsx b/03_source/mobile/src/App.tsx index b394624..fa050bf 100644 --- a/03_source/mobile/src/App.tsx +++ b/03_source/mobile/src/App.tsx @@ -47,6 +47,7 @@ import { loadConfData } from './data/sessions/sessions.actions'; import { setIsLoggedIn, setUsername, loadUserData } from './data/user/user.actions'; import Account from './pages/Account'; import Login from './pages/Login'; +import MyLogin from './pages/MyLogin'; import Signup from './pages/Signup'; import Support from './pages/Support'; import Tutorial from './pages/Tutorial'; @@ -88,10 +89,18 @@ interface DispatchProps { interface IonicAppProps extends StateProps, DispatchProps {} -const IonicApp: React.FC = ({ darkMode, schedule, setIsLoggedIn, setUsername, loadConfData, loadUserData }) => { +const IonicApp: React.FC = ({ + darkMode, + schedule, + setIsLoggedIn, + setUsername, + loadConfData, + loadUserData, +}) => { useEffect(() => { loadUserData(); loadConfData(); + // eslint-disable-next-line }, []); @@ -112,18 +121,15 @@ const IonicApp: React.FC = ({ darkMode, schedule, setIsLoggedIn, {/* */} } /> + + {/* */} + + - {/* */} - - {/* */} - - - - {/* */} { return ( @@ -9,8 +22,18 @@ const AppRoute: React.FC = () => { {/* */} - - + + + + {/* component make the ":id" available in the "OrderDetail" */} + + {/* */} + + {/* */} + + + + ); }; diff --git a/03_source/mobile/src/TabAppRoute.tsx b/03_source/mobile/src/TabAppRoute.tsx index 99463d4..b66681a 100644 --- a/03_source/mobile/src/TabAppRoute.tsx +++ b/03_source/mobile/src/TabAppRoute.tsx @@ -9,11 +9,12 @@ import MessageList from './pages/MessageList'; import Favourites from './pages/Favourites'; import MyProfile from './pages/MyProfile'; import EventList from './pages/EventList'; +import Helloworld from './pages/Helloworld'; const TabAppRoute: React.FC = () => { return ( <> - + {/* */} } exact={true} /> @@ -28,10 +29,10 @@ const TabAppRoute: React.FC = () => { } exact={true} /> {/* */} - } exact={true} /> + } exact={true} /> {/* */} - } exact={true} /> + } exact={true} /> ); }; diff --git a/03_source/mobile/src/components/HomeOrTutorial.tsx b/03_source/mobile/src/components/HomeOrTutorial.tsx index 210097a..8d4f61e 100644 --- a/03_source/mobile/src/components/HomeOrTutorial.tsx +++ b/03_source/mobile/src/components/HomeOrTutorial.tsx @@ -7,11 +7,7 @@ interface StateProps { } const HomeOrTutorial: React.FC = ({ hasSeenTutorial }) => { - return hasSeenTutorial ? ( - - ) : ( - - ); + return hasSeenTutorial ? : ; }; export default connect<{}, StateProps, {}>({ diff --git a/03_source/mobile/src/components/RedirectToLogin.tsx b/03_source/mobile/src/components/RedirectToLogin.tsx index 7eda327..ca86a65 100644 --- a/03_source/mobile/src/components/RedirectToLogin.tsx +++ b/03_source/mobile/src/components/RedirectToLogin.tsx @@ -6,15 +6,12 @@ interface RedirectToLoginProps { setUsername: Function; } -const RedirectToLogin: React.FC = ({ - setIsLoggedIn, - setUsername, -}) => { +const RedirectToLogin: React.FC = ({ setIsLoggedIn, setUsername }) => { const ionRouterContext = useContext(IonRouterContext); useEffect(() => { setIsLoggedIn(false); setUsername(undefined); - ionRouterContext.push('/tabs/schedule'); + ionRouterContext.push('/tabs/events'); }, [setIsLoggedIn, setUsername]); return null; }; diff --git a/03_source/mobile/src/context/action.tsx b/03_source/mobile/src/context/action.tsx new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/context/index.ts b/03_source/mobile/src/context/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/context/jwt/action.tsx b/03_source/mobile/src/context/jwt/action.tsx new file mode 100644 index 0000000..e091237 --- /dev/null +++ b/03_source/mobile/src/context/jwt/action.tsx @@ -0,0 +1,92 @@ +import axios, { endpoints } from '../../lib/axios'; + +import { setSession } from './utils'; +import { JWT_STORAGE_KEY } from './constant'; + +// ---------------------------------------------------------------------- + +export type SignInParams = { + email: string; + password: string; +}; + +export type SignUpParams = { + email: string; + password: string; + firstName: string; + lastName: string; +}; + +const ERR_ACCESS_TOKEN_NOT_FOUND = `Access token not found in response`; + +/** ************************************** + * Sign in + *************************************** */ +export const signInWithPassword = async ({ + email, + password, +}: SignInParams): Promise => { + try { + const params = { email, password }; + + const res = await axios.post(endpoints.auth.signIn, params); + + const { accessToken } = res.data; + + console.log({ t: res.data }); + + if (!accessToken) { + throw new Error(ERR_ACCESS_TOKEN_NOT_FOUND); + } + + // setSession(accessToken); + return accessToken; + } catch (error) { + console.error('Error during sign in:', error); + throw error; + } +}; + +/** ************************************** + * Sign up + *************************************** */ +export const signUp = async ({ + email, + password, + firstName, + lastName, +}: SignUpParams): Promise => { + const params = { + email, + password, + firstName, + lastName, + }; + + try { + const res = await axios.post(endpoints.auth.signUp, params); + + const { accessToken } = res.data; + + if (!accessToken) { + throw new Error('Access token not found in response'); + } + + sessionStorage.setItem(JWT_STORAGE_KEY, accessToken); + } catch (error) { + console.error('Error during sign up:', error); + throw error; + } +}; + +/** ************************************** + * Sign out + *************************************** */ +export const signOut = async (): Promise => { + try { + await setSession(null); + } catch (error) { + console.error('Error during sign out:', error); + throw error; + } +}; diff --git a/03_source/mobile/src/context/jwt/constant.ts b/03_source/mobile/src/context/jwt/constant.ts new file mode 100644 index 0000000..c9cb827 --- /dev/null +++ b/03_source/mobile/src/context/jwt/constant.ts @@ -0,0 +1 @@ +export const JWT_STORAGE_KEY = 'jwt_access_token'; diff --git a/03_source/mobile/src/context/jwt/utils.tsx b/03_source/mobile/src/context/jwt/utils.tsx new file mode 100644 index 0000000..e41fd22 --- /dev/null +++ b/03_source/mobile/src/context/jwt/utils.tsx @@ -0,0 +1,97 @@ +// import { paths } from 'src/routes/paths'; + +import axios from '../../lib/axios'; + +import { JWT_STORAGE_KEY } from './constant.js'; +import paths from '../../paths.js'; + +// ---------------------------------------------------------------------- + +export function jwtDecode(token: string) { + try { + if (!token) return null; + + const parts = token.split('.'); + if (parts.length < 2) { + throw new Error('Invalid token!'); + } + + const base64Url = parts[1]; + const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); + const decoded = JSON.parse(atob(base64)); + + return decoded; + } catch (error) { + console.error('Error decoding token:', error); + throw error; + } +} + +// ---------------------------------------------------------------------- + +export function isValidToken(accessToken: string) { + if (!accessToken) { + return false; + } + + try { + const decoded = jwtDecode(accessToken); + + if (!decoded || !('exp' in decoded)) { + return false; + } + + const currentTime = Date.now() / 1000; + + return decoded.exp > currentTime; + } catch (error) { + console.error('Error during token validation:', error); + return false; + } +} + +// ---------------------------------------------------------------------- + +export function tokenExpired(exp: number) { + const currentTime = Date.now(); + const timeLeft = exp * 1000 - currentTime; + + setTimeout(() => { + try { + alert('Token expired!'); + sessionStorage.removeItem(JWT_STORAGE_KEY); + window.location.href = paths.SIGN_IN; + } catch (error) { + console.error('Error during token expiration:', error); + throw error; + } + }, timeLeft); +} + +// ---------------------------------------------------------------------- + +const INVALID_ACCESS_TOKEN = 'Invalid access token!'; + +export async function setSession(accessToken: string | null) { + try { + if (accessToken) { + sessionStorage.setItem(JWT_STORAGE_KEY, accessToken); + + axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`; + + const decodedToken = jwtDecode(accessToken); // ~3 days by minimals server + + if (decodedToken && 'exp' in decodedToken) { + tokenExpired(decodedToken.exp); + } else { + throw new Error(INVALID_ACCESS_TOKEN); + } + } else { + sessionStorage.removeItem(JWT_STORAGE_KEY); + delete axios.defaults.headers.common.Authorization; + } + } catch (error) { + console.error('Error during set session:', error); + throw error; + } +} diff --git a/03_source/mobile/src/data/AppContext.tsx b/03_source/mobile/src/data/AppContext.tsx index 45777d5..d1375ad 100644 --- a/03_source/mobile/src/data/AppContext.tsx +++ b/03_source/mobile/src/data/AppContext.tsx @@ -11,9 +11,7 @@ export const AppContext = createContext({ dispatch: () => undefined, }); -export const AppContextProvider: React.FC = ({ - children, -}) => { +export const AppContextProvider: React.FC = ({ children }) => { const [store, dispatch] = useReducer(reducers, initialState); return ( diff --git a/03_source/mobile/src/data/dataApi.ts b/03_source/mobile/src/data/dataApi.ts index fdb26bd..e08558d 100644 --- a/03_source/mobile/src/data/dataApi.ts +++ b/03_source/mobile/src/data/dataApi.ts @@ -1,4 +1,4 @@ -import { Preferences as Storage } from '@capacitor/preferences'; +import { GetResult, Preferences as Storage } from '@capacitor/preferences'; import { Schedule, Session } from '../models/Schedule'; import { Speaker } from '../models/Speaker'; import { Location } from '../models/Location'; @@ -13,6 +13,8 @@ const locationsUrl = '/assets/data/locations.json'; const HAS_LOGGED_IN = 'hasLoggedIn'; const HAS_SEEN_TUTORIAL = 'hasSeenTutorial'; const USERNAME = 'username'; +const ACCESS_TOKEN = 'a_token'; +const ACTIVE_SESSION = 'a_session'; export const getConfData = async () => { const response = await Promise.all([ @@ -116,6 +118,18 @@ export const setUsernameData = async (username?: string) => { } }; +export const setAccessTokenData = async (accessToken?: string) => { + if (!accessToken) { + await Storage.remove({ key: ACCESS_TOKEN }); + } else { + await Storage.set({ key: ACCESS_TOKEN, value: accessToken }); + } +}; + +export const getAccessTokenData = async (): Promise => { + return Storage.get({ key: ACCESS_TOKEN }); +}; + function parseSessions(schedule: Schedule) { const sessions: Session[] = []; schedule.groups.forEach((g) => { @@ -123,3 +137,15 @@ function parseSessions(schedule: Schedule) { }); return sessions; } + +export const setActiveSessionData = async (activeSession: any) => { + if (!activeSession) { + await Storage.remove({ key: ACTIVE_SESSION }); + } else { + await Storage.set({ key: ACTIVE_SESSION, value: JSON.stringify(activeSession) }); + } +}; + +export const getActiveSessionData = async (): Promise => { + return Storage.get({ key: JSON.parse(ACTIVE_SESSION) }); +}; diff --git a/03_source/mobile/src/data/state.ts b/03_source/mobile/src/data/state.ts index 0302297..0680912 100644 --- a/03_source/mobile/src/data/state.ts +++ b/03_source/mobile/src/data/state.ts @@ -28,6 +28,8 @@ export const initialState: AppState = { darkMode: false, isLoggedin: false, loading: false, + // + isSessionValid: false, }, locations: { locations: [], diff --git a/03_source/mobile/src/data/user/user.actions.ts b/03_source/mobile/src/data/user/user.actions.ts index a132470..0aecc72 100644 --- a/03_source/mobile/src/data/user/user.actions.ts +++ b/03_source/mobile/src/data/user/user.actions.ts @@ -3,9 +3,15 @@ import { setIsLoggedInData, setUsernameData, setHasSeenTutorialData, + setAccessTokenData, + getAccessTokenData, + setActiveSessionData, } from '../dataApi'; import { ActionType } from '../../util/types'; import { UserState } from './user.state'; +import { isValidToken } from '../../context/jwt/utils'; +import axios from 'axios'; +import { endpoints } from '../../pages/MyLogin/endpoints'; export const loadUserData = () => async (dispatch: React.Dispatch) => { dispatch(setLoading(true)); @@ -15,10 +21,7 @@ export const loadUserData = () => async (dispatch: React.Dispatch) => { }; export const setLoading = (isLoading: boolean) => - ({ - type: 'set-user-loading', - isLoading, - } as const); + ({ type: 'set-user-loading', isLoading } as const); export const setData = (data: Partial) => ({ @@ -47,6 +50,53 @@ export const setUsername = (username?: string) => async (dispatch: React.Dispatc } as const; }; +export const setAccessToken = (token?: string) => async (dispatch: React.Dispatch) => { + await setAccessTokenData(token); + return { + type: 'set-access-token', + token, + } as const; +}; + +export const setActiveSession = (session: any) => async (dispatch: React.Dispatch) => { + await setActiveSessionData(session); + return { + type: 'set-active-session', + session, + } as const; +}; + +export const checkUserSession = () => async (dispatch: React.Dispatch) => { + let accessToken = (await getAccessTokenData()).value; + console.log('check user session'); + let sessionValid = false; + + try { + if (accessToken && isValidToken(accessToken)) { + const res = await axios.get(endpoints.auth.me, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + + const { user } = res.data; + + setActiveSession({ user: { ...user, accessToken }, loading: false }); + sessionValid = true; + console.log('session valid'); + } else { + setActiveSession({ user: null, loading: false }); + console.log('session not valid'); + } + } catch (error) { + console.error(error); + setActiveSession({ user: null, loading: false }); + } + + return { + type: 'check-user-session', + sessionValid, + } as const; +}; + export const setHasSeenTutorial = (hasSeenTutorial: boolean) => async (dispatch: React.Dispatch) => { await setHasSeenTutorialData(hasSeenTutorial); @@ -68,4 +118,7 @@ export type UserActions = | ActionType | ActionType | ActionType - | ActionType; + | ActionType + | ActionType + // | ActionType + | ActionType; diff --git a/03_source/mobile/src/data/user/user.reducer.ts b/03_source/mobile/src/data/user/user.reducer.ts index 9f0247d..b378c42 100644 --- a/03_source/mobile/src/data/user/user.reducer.ts +++ b/03_source/mobile/src/data/user/user.reducer.ts @@ -15,5 +15,11 @@ export function userReducer(state: UserState, action: UserActions): UserState { return { ...state, darkMode: action.darkMode }; case 'set-is-loggedin': return { ...state, isLoggedin: action.loggedIn }; + case 'check-user-session': + return { ...state, isSessionValid: action.sessionValid }; + // case 'set-active-session': + // return { ...state, session: action.session }; + // case 'set-access-token': + // return { ...state, token: action.token }; } } diff --git a/03_source/mobile/src/data/user/user.state.ts b/03_source/mobile/src/data/user/user.state.ts index e8a1f8e..aa2460c 100644 --- a/03_source/mobile/src/data/user/user.state.ts +++ b/03_source/mobile/src/data/user/user.state.ts @@ -4,4 +4,7 @@ export interface UserState { darkMode: boolean; hasSeenTutorial: boolean; loading: boolean; + isSessionValid: boolean; + session?: any; + token?: string; } diff --git a/03_source/mobile/src/global-config.ts b/03_source/mobile/src/global-config.ts new file mode 100644 index 0000000..71b4f35 --- /dev/null +++ b/03_source/mobile/src/global-config.ts @@ -0,0 +1,3 @@ +export const CONFIG = { + serverUrl: '', +}; diff --git a/03_source/mobile/src/hooks/use-set-state.ts b/03_source/mobile/src/hooks/use-set-state.ts new file mode 100644 index 0000000..f820bd2 --- /dev/null +++ b/03_source/mobile/src/hooks/use-set-state.ts @@ -0,0 +1,32 @@ +/** + * Custom hook to manage state with utility functions to set state, set a specific field, and reset state. + * + * @param {T} initialState - The initial state value. + * + * @returns {UseSetStateReturn} - An object containing: + * - `state`: The current state. + * - `resetState`: A function to reset the state to the initial value. + * - `setState`: A function to update the state. + * - `setField`: A function to update a specific field in the state. + * + * @example + * const { state, setState, setField, resetState } = useSetState({ name: '', age: 0 }); + * + * return ( + *
+ *

Name: {state.name}

+ *

Age: {state.age}

+ * + * + *
+ * ); + */ +type UseSetStateReturn = { + state: T; + resetState: (defaultState?: T) => void; + setState: (updateState: T | Partial) => void; + setField: (name: keyof T, updateValue: T[keyof T]) => void; +}; +declare function useSetState(initialState?: T): UseSetStateReturn; + +export { type UseSetStateReturn, useSetState }; diff --git a/03_source/mobile/src/pages/Helloworld/index.tsx b/03_source/mobile/src/pages/Helloworld/index.tsx new file mode 100644 index 0000000..b8e7438 --- /dev/null +++ b/03_source/mobile/src/pages/Helloworld/index.tsx @@ -0,0 +1,27 @@ +// REQ0041/home_discover_event_tab + +import { IonPage, IonHeader, IonToolbar, IonButtons, IonButton, IonIcon, IonTitle, IonContent } from '@ionic/react'; +import { menuOutline } from 'ionicons/icons'; +import React, { useEffect, useRef, useState } from 'react'; + +const Helloworld: React.FC = () => { + return ( + + + + + {/* */} + + + + + Discover Events + + + + Helloworld + + ); +}; + +export default Helloworld; diff --git a/03_source/mobile/src/pages/MainTabs.tsx b/03_source/mobile/src/pages/MainTabs.tsx index 5b337a8..bf2d8dd 100644 --- a/03_source/mobile/src/pages/MainTabs.tsx +++ b/03_source/mobile/src/pages/MainTabs.tsx @@ -26,7 +26,7 @@ const MainTabs: React.FC = () => { {/* REQ0117/default-route */} - + {/* Using the render method prop cuts down the number of renders your components will have due to route changes. Use the component prop when your component depends on the RouterComponentProps passed in automatically. @@ -68,7 +68,7 @@ const MainTabs: React.FC = () => { Message - + Profile diff --git a/03_source/mobile/src/pages/MyLogin/endpoints.ts b/03_source/mobile/src/pages/MyLogin/endpoints.ts new file mode 100644 index 0000000..9b9c87b --- /dev/null +++ b/03_source/mobile/src/pages/MyLogin/endpoints.ts @@ -0,0 +1,12 @@ +const CMS_BACKEND_URL = 'http://192.168.10.75:7272'; + +const endpoints = { + auth: { + me: `http://localhost:7272/api/auth/me`, + signIn: `${CMS_BACKEND_URL}/api/auth/sign-in`, + signUp: `${CMS_BACKEND_URL}/api/auth/sign-up`, + // + }, +}; + +export { endpoints }; diff --git a/03_source/mobile/src/pages/MyLogin/index.tsx b/03_source/mobile/src/pages/MyLogin/index.tsx new file mode 100644 index 0000000..591e107 --- /dev/null +++ b/03_source/mobile/src/pages/MyLogin/index.tsx @@ -0,0 +1,269 @@ +import { z as zod } from 'zod'; +import React, { useEffect, useState } from 'react'; +import { useForm, Controller } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useLocalStorage } from 'react-use'; + +import { + IonHeader, + IonToolbar, + IonTitle, + IonContent, + IonPage, + IonButtons, + IonMenuButton, + IonRow, + IonCol, + IonButton, + IonInput, + IonIcon, + useIonRouter, +} from '@ionic/react'; + +import { useHistory } from 'react-router'; +import './style.scss'; + +import { + setAccessToken, + setIsLoggedIn, + setUsername, + checkUserSession, +} from '../../data/user/user.actions'; +import { connect } from '../../data/connect'; + +import { chevronBack, chevronBackOutline } from 'ionicons/icons'; + +import { signInWithPassword } from '../../context/jwt/action'; +import axios from 'axios'; +import { endpoints } from './endpoints'; + +export type SignInSchemaType = zod.infer; + +export const SignInSchema = zod.object({ + email: zod + .string() + .min(1, { message: 'Email is required!' }) + .email({ message: 'Email must be a valid email address!' }), + password: zod + .string() + .min(1, { message: 'Password is required!' }) + .min(6, { message: 'Password must be at least 6 characters!' }), +}); + +interface OwnProps {} + +interface DispatchProps { + setIsLoggedIn: typeof setIsLoggedIn; + setUsername: typeof setUsername; + setAccessToken: typeof setAccessToken; + checkUserSession: typeof checkUserSession; +} + +interface StateProps { + isSessionValid: boolean; +} + +interface LoginProps extends OwnProps, StateProps, DispatchProps {} + +type UserType = Record | null; + +const Login: React.FC = (props) => { + const { + setAccessToken, + setIsLoggedIn, + setUsername: setUsernameAction, + checkUserSession, + isSessionValid, + } = props; + + const history = useHistory(); + + // TODO: delete unused code + // const [login, setLogin] = useState({ email: '', password: '' }); + // const [submitted, setSubmitted] = useState(false); + // const onLogin = async (e: React.FormEvent) => { + // e.preventDefault(); + // setSubmitted(true); + + // if (login.email && login.password) { + // await setIsLoggedIn(true); + // await login.email; + // history.push('/tabs/events'); + // } + // }; + + const router = useIonRouter(); + function handleBackButtonClick() { + router.goBack(); + } + + const onSignup = () => { + history.push('/signup'); + }; + + // ---------- + + const defaultValues: SignInSchemaType = { + email: '', + password: '', + }; + + const methods = useForm({ + resolver: zodResolver(SignInSchema), + defaultValues, + }); + + const { + reset, + watch, + setValue, + handleSubmit, + register, + formState: { isDirty, dirtyFields, errors, isSubmitting }, + } = methods; + + const values = watch(); + + useEffect(() => { + (async () => { + console.log({ isSessionValid }); + if (isSessionValid) { + await setIsLoggedIn(true); + router.push('/tabs'); + // reset(); + reset(); + } + })(); + }, [isSessionValid]); + + const onSubmit = handleSubmit(async (data) => { + console.log({ data }); + try { + let token = await signInWithPassword({ email: values.email, password: values.password }); + console.log({ token }); + if (token) setAccessToken(token); + + await checkUserSession(); + + // NOTE: page forward handled by changing of state `isSessionValid` + } catch (error) { + console.error(error); + // const feedbackMessage = getErrorMessage(error); + // setErrorMessage(feedbackMessage); + } + }); + + const { + onChange: emailOnChange, + onBlur: emailOnBlur, + name: emailName, + ref: emailRef, + } = register('email'); + const { + onChange: passwordOnChange, + onBlur: passwordOnBlur, + name: passwordName, + ref: passwordRef, + } = register('password'); + + return ( + + + + handleBackButtonClick()} + > + + + Login + + + {/* */} + +
+ Ionic logo +
+ +
+
+ {/* // ${!errors.email && 'ion-valid'} */} + setValue('email', e.detail.value!)} + {...register('email')} + /> + + {/* // ${!errors.password && 'ion-valid'} */} + setValue('password', e.detail.value!)} + required + disabled={isSubmitting} + {...register('password')} + /> + + + + + {isSubmitting ? 'logging in' : 'Login'} + + + + + Signup + + + + +
+
+
+ ); +}; + +export default connect<{}, StateProps, DispatchProps>({ + mapStateToProps: (state) => ({ + isSessionValid: state.user.isSessionValid, + }), + mapDispatchToProps: { + setIsLoggedIn, + setUsername, + setAccessToken, + checkUserSession, + }, + component: Login, +}); diff --git a/03_source/mobile/src/pages/MyLogin/isValidToken.tsx b/03_source/mobile/src/pages/MyLogin/isValidToken.tsx new file mode 100644 index 0000000..46ea5fd --- /dev/null +++ b/03_source/mobile/src/pages/MyLogin/isValidToken.tsx @@ -0,0 +1,22 @@ +import { jwtDecode } from './jwtDecode'; + +function isValidToken(accessToken: string) { + if (!accessToken) { + return false; + } + + try { + const decoded = jwtDecode(accessToken); + + if (!decoded || !('exp' in decoded)) { + return false; + } + + const currentTime = Date.now() / 1000; + + return decoded.exp > currentTime; + } catch (error) { + console.error('Error during token validation:', error); + return false; + } +} diff --git a/03_source/mobile/src/pages/MyLogin/jwtDecode.tsx b/03_source/mobile/src/pages/MyLogin/jwtDecode.tsx new file mode 100644 index 0000000..f1c37fb --- /dev/null +++ b/03_source/mobile/src/pages/MyLogin/jwtDecode.tsx @@ -0,0 +1,19 @@ +export function jwtDecode(token: string) { + try { + if (!token) return null; + + const parts = token.split('.'); + if (parts.length < 2) { + throw new Error('Invalid token!'); + } + + const base64Url = parts[1]; + const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); + const decoded = JSON.parse(atob(base64)); + + return decoded; + } catch (error) { + console.error('Error decoding token:', error); + throw error; + } +} diff --git a/03_source/mobile/src/pages/MyLogin/style.scss b/03_source/mobile/src/pages/MyLogin/style.scss new file mode 100644 index 0000000..9a56dc7 --- /dev/null +++ b/03_source/mobile/src/pages/MyLogin/style.scss @@ -0,0 +1,23 @@ +#login-page, #signup-page, #support-page { + .login-logo { + min-height: 200px; + padding: 20px 0; + text-align: center; + } + + .login-logo img { + max-width: 150px; + } + + .list { + margin-bottom: 0; + } + + .login-form { + padding: 16px; + } + + ion-input { + margin-bottom: 10px; + } +} diff --git a/03_source/mobile/src/pages/MyProfile/NotLoggedIn/index.tsx b/03_source/mobile/src/pages/MyProfile/NotLoggedIn/index.tsx index a4e7252..5b0d5aa 100644 --- a/03_source/mobile/src/pages/MyProfile/NotLoggedIn/index.tsx +++ b/03_source/mobile/src/pages/MyProfile/NotLoggedIn/index.tsx @@ -44,7 +44,14 @@ import '../../SpeakerList.scss'; import { getEvents } from '../../../api/getEvents'; import { format } from 'date-fns'; import { Event } from '../types'; -import { alertOutline, chevronDownCircleOutline, createOutline, heart, menuOutline, settingsOutline } from 'ionicons/icons'; +import { + alertOutline, + chevronDownCircleOutline, + createOutline, + heart, + menuOutline, + settingsOutline, +} from 'ionicons/icons'; import AboutPopover from '../../../components/AboutPopover'; import paths from '../../../paths'; import { getProfileById } from '../../../api/getProfileById'; @@ -89,7 +96,7 @@ const MyProfile: React.FC = ({ speakers, speakerSessions }) => function handleForwardLoginPage() { try { setDisableForwardLoginButton(true); - router.push(paths.login); + router.push(paths.SIGN_IN); setDisableForwardLoginButton(false); } catch (error) { console.error(error); @@ -121,7 +128,12 @@ const MyProfile: React.FC = ({ speakers, speakerSessions }) => - + @@ -130,12 +142,28 @@ const MyProfile: React.FC = ({ speakers, speakerSessions }) => -
+
not login yet,
please login or sign up
-
+
Login diff --git a/03_source/mobile/src/pages/SpeakerDetail.tsx b/03_source/mobile/src/pages/SpeakerDetail.tsx index a89095f..3e0cd8d 100644 --- a/03_source/mobile/src/pages/SpeakerDetail.tsx +++ b/03_source/mobile/src/pages/SpeakerDetail.tsx @@ -44,9 +44,7 @@ interface SpeakerDetailProps extends OwnProps, StateProps, DispatchProps {} const SpeakerDetail: React.FC = ({ speaker }) => { const [showActionSheet, setShowActionSheet] = useState(false); - const [actionSheetButtons, setActionSheetButtons] = useState< - ActionSheetButton[] - >([]); + const [actionSheetButtons, setActionSheetButtons] = useState([]); const [actionSheetHeader, setActionSheetHeader] = useState(''); function openSpeakerShare(speaker: Speaker) { @@ -112,18 +110,10 @@ const SpeakerDetail: React.FC = ({ speaker }) => { openContact(speaker)}> - + openSpeakerShare(speaker)}> - + @@ -141,9 +131,7 @@ const SpeakerDetail: React.FC = ({ speaker }) => { - openExternalUrl(`https://twitter.com/${speaker.twitter}`) - } + onClick={() => openExternalUrl(`https://twitter.com/${speaker.twitter}`)} > Twitter @@ -151,9 +139,7 @@ const SpeakerDetail: React.FC = ({ speaker }) => { - openExternalUrl('https://github.com/ionic-team/ionic-framework') - } + onClick={() => openExternalUrl('https://github.com/ionic-team/ionic-framework')} > GitHub @@ -161,9 +147,7 @@ const SpeakerDetail: React.FC = ({ speaker }) => { - openExternalUrl('https://instagram.com/ionicframework') - } + onClick={() => openExternalUrl('https://instagram.com/ionicframework')} > Instagram diff --git a/03_source/mobile/src/paths.ts b/03_source/mobile/src/paths.ts index 2b855fd..4ae423b 100644 --- a/03_source/mobile/src/paths.ts +++ b/03_source/mobile/src/paths.ts @@ -1,17 +1,25 @@ const paths = { NOT_IMPLEMENTED: '/not_implemented', + TAB_NOT_IMPLEMENTED: '/tabs/not_implemented', + // SETTINGS: '/settings', // EVENT_LIST: `/tabs/events`, MESSAGE_LIST: `/tabs/messages`, NEARBY_LIST: '/tabs/nearby', + // ORDERS_LIST: '/tabs/orders', - FAVOURITES_LIST: `/tabs/favourites`, + // + ORDER_DETAIL: '/order_detail/:id', getOrderDetail: (id: string) => `/order_detail/${id}`, + // + FAVOURITES_LIST: `/tabs/favourites`, CHANGE_LANGUAGE: '/change_language', SERVICE_AGREEMENT: '/service_agreement', PRIVACY_AGREEMENT: '/privacy_agreement', // - login: '/login', + PROFILE: '/tabs/my_profile', + // + SIGN_IN: '/mylogin', }; export default paths; diff --git a/03_source/mobile/yarn.lock b/03_source/mobile/yarn.lock index 43b3f4c..dcc335f 100644 --- a/03_source/mobile/yarn.lock +++ b/03_source/mobile/yarn.lock @@ -225,6 +225,13 @@ resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz" integrity sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA== +"@hookform/resolvers@^4.1.3": + version "4.1.3" + resolved "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-4.1.3.tgz" + integrity sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ== + dependencies: + "@standard-schema/utils" "^0.3.0" + "@ionic/cli-framework-output@^2.2.8": version "2.2.8" resolved "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz" @@ -385,7 +392,7 @@ resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": version "1.5.0" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== @@ -464,6 +471,11 @@ resolved "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz" integrity sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw== +"@standard-schema/utils@^0.3.0": + version "0.3.0" + resolved "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz" + integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g== + "@stencil/core@^4.0.3", "@stencil/core@4.20.0": version "4.20.0" resolved "https://registry.npmjs.org/@stencil/core/-/core-4.20.0.tgz" @@ -607,6 +619,11 @@ dependencies: "@types/jest-diff" "*" +"@types/js-cookie@^2.2.6": + version "2.2.7" + resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz" + integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA== + "@types/leaflet@^1.9.17": version "1.9.18" resolved "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.18.tgz" @@ -754,6 +771,11 @@ resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz" integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== +"@xobotyi/scrollbar-width@^1.9.5": + version "1.9.5" + resolved "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz" + integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== + ansi-escapes@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz" @@ -1017,6 +1039,13 @@ convert-source-map@^2.0.0: resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +copy-to-clipboard@^3.3.1: + version "3.3.3" + resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== + dependencies: + toggle-selection "^1.0.6" + core-js-pure@^3.30.2: version "3.42.0" resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.42.0.tgz" @@ -1031,7 +1060,22 @@ cross-spawn@^7.0.3, cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" -csstype@^3.0.2: +css-in-js-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz" + integrity sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A== + dependencies: + hyphenate-style-name "^1.0.3" + +css-tree@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +csstype@^3.0.2, csstype@^3.1.2: version "3.1.3" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== @@ -1137,6 +1181,13 @@ env-paths@^2.2.0: resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + es-define-property@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" @@ -1230,6 +1281,21 @@ extend@^3.0.0: resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== + +fastest-stable-stringify@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz" + integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q== + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" @@ -1445,6 +1511,11 @@ human-signals@^4.3.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz" integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== +hyphenate-style-name@^1.0.3: + version "1.1.0" + resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz" + integrity sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw== + immutable@^5.0.2: version "5.1.2" resolved "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz" @@ -1465,6 +1536,13 @@ inline-style-parser@0.2.4: resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz" integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== +inline-style-prefixer@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz" + integrity sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw== + dependencies: + css-in-js-utils "^3.1.0" + ionicons@^7.0.0, ionicons@^7.1.2, ionicons@^7.2.2: version "7.4.0" resolved "https://registry.npmjs.org/ionicons/-/ionicons-7.4.0.tgz" @@ -1561,6 +1639,11 @@ jackspeak@^4.0.1: dependencies: "@isaacs/cliui" "^8.0.2" +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" @@ -1778,6 +1861,11 @@ mdast-util-to-string@^4.0.0: dependencies: "@types/mdast" "^4.0.0" +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" @@ -2054,6 +2142,20 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +nano-css@^5.6.2: + version "5.6.2" + resolved "https://registry.npmjs.org/nano-css/-/nano-css-5.6.2.tgz" + integrity sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + css-tree "^1.1.2" + csstype "^3.1.2" + fastest-stable-stringify "^2.0.2" + inline-style-prefixer "^7.0.1" + rtl-css-js "^1.16.1" + stacktrace-js "^2.0.2" + stylis "^4.3.0" + nanoid@^3.3.8: version "3.3.11" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" @@ -2266,6 +2368,11 @@ react-dom@*, "react-dom@^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", react-dom@^19 dependencies: scheduler "^0.25.0" +react-hook-form@^7.0.0, react-hook-form@^7.55.0: + version "7.57.0" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.57.0.tgz" + integrity sha512-RbEks3+cbvTP84l/VXGUZ+JMrKOS8ykQCRYdm5aYsxnDquL0vspsyNhGRO7pcH6hsZqWlPOjLye7rJqdtdAmlg== + react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.4: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" @@ -2341,7 +2448,32 @@ react-spinners@^0.17.0: resolved "https://registry.npmjs.org/react-spinners/-/react-spinners-0.17.0.tgz" integrity sha512-L/8HTylaBmIWwQzIjMq+0vyaRXuoAevzWoD35wKpNTxxtYXWZp+xtgkfD7Y4WItuX0YvdxMPU79+7VhhmbmuTQ== -react@*, "react@^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^18.0 || ^19", react@^19.0.0, react@>=15, react@>=16, react@>=16.8.6, react@>=18, react@19.0.0: +react-universal-interface@^0.6.2: + version "0.6.2" + resolved "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz" + integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== + +react-use@^17.6.0: + version "17.6.0" + resolved "https://registry.npmjs.org/react-use/-/react-use-17.6.0.tgz" + integrity sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g== + dependencies: + "@types/js-cookie" "^2.2.6" + "@xobotyi/scrollbar-width" "^1.9.5" + copy-to-clipboard "^3.3.1" + fast-deep-equal "^3.1.3" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.6.2" + react-universal-interface "^0.6.2" + resize-observer-polyfill "^1.5.1" + screenfull "^5.1.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^3.0.1" + ts-easing "^0.2.0" + tslib "^2.1.0" + +react@*, "react@^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^18.0 || ^19", react@^19.0.0, react@>=15, react@>=16, react@>=16.8.6, react@>=18, react@19.0.0: version "19.0.0" resolved "https://registry.npmjs.org/react/-/react-19.0.0.tgz" integrity sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ== @@ -2398,6 +2530,11 @@ reselect@^4.0.0: resolved "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz" integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-pathname@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz" @@ -2453,6 +2590,13 @@ rollup@^4.34.9: "@rollup/rollup-win32-x64-msvc" "4.41.1" fsevents "~2.3.2" +rtl-css-js@^1.16.1: + version "1.16.1" + resolved "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz" + integrity sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg== + dependencies: + "@babel/runtime" "^7.1.2" + safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" @@ -2479,6 +2623,11 @@ scheduler@^0.25.0: resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz" integrity sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA== +screenfull@^5.1.0: + version "5.2.0" + resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz" + integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== + semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" @@ -2489,6 +2638,11 @@ semver@^7.6.3: resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" @@ -2538,6 +2692,16 @@ source-map-js@^1.2.1, "source-map-js@>=0.6.2 <2.0.0": resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA== + space-separated-tokens@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz" @@ -2553,6 +2717,35 @@ ssr-window@^4.0.2: resolved "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz" integrity sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ== +stack-generator@^2.0.5: + version "2.0.10" + resolved "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz" + integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ== + dependencies: + stackframe "^1.3.4" + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +stacktrace-gps@^3.0.4: + version "3.1.2" + resolved "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz" + integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ== + dependencies: + source-map "0.5.6" + stackframe "^1.3.4" + +stacktrace-js@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz" + integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== + dependencies: + error-stack-parser "^2.0.6" + stack-generator "^2.0.5" + stacktrace-gps "^3.0.4" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" @@ -2649,6 +2842,11 @@ style-to-object@1.0.8: dependencies: inline-style-parser "0.2.4" +stylis@^4.3.0: + version "4.3.6" + resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz" + integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" @@ -2675,6 +2873,11 @@ tar@^6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" +throttle-debounce@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz" + integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== + through2@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz" @@ -2707,6 +2910,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" @@ -2722,6 +2930,11 @@ trough@^2.0.0: resolved "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz" integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + tslib@*, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.6.2, tslib@^2.8.1: version "2.8.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" @@ -2950,6 +3163,11 @@ yauzl@^2.10.0: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" +zod@^3.24.2: + version "3.25.46" + resolved "https://registry.npmjs.org/zod/-/zod-3.25.46.tgz" + integrity sha512-IqRxcHEIjqLd4LNS/zKffB3Jzg3NwqJxQQ0Ns7pdrvgGkwQsEBdEQcOHaBVqvvZArShRzI39+aMST3FBGmTrLQ== + zwitch@^2.0.0: version "2.0.4" resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz"