diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 047e0538d..d64688860 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -11,17 +11,19 @@ jobs: node-version: '23.x' - uses: pnpm/action-setup@v2 with: - version: 8 + version: 10 - run: pnpm install - name: Build packages run: | pnpm --filter @courselit/icons build + pnpm --filter @courselit/page-models build pnpm --filter @courselit/common-models build pnpm --filter @courselit/utils build pnpm --filter @courselit/text-editor build pnpm --filter @courselit/state-management build pnpm --filter @courselit/components-library build - pnpm --filter @courselit/common-widgets build + pnpm --filter @courselit/page-primitives build + pnpm --filter @courselit/page-blocks build - name: Running tests run: | pnpm test diff --git a/.gitignore b/.gitignore index e389d43f7..2c85a6c61 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ coverage # Text editors configurations .vscode .rgignore +.cursor # Env file .env*.local diff --git a/.yarn/cache/@adobe-css-tools-npm-4.4.2-e89b531279-ecc9f626fa.zip b/.yarn/cache/@adobe-css-tools-npm-4.4.2-e89b531279-ecc9f626fa.zip deleted file mode 100644 index 875344aa2..000000000 Binary files a/.yarn/cache/@adobe-css-tools-npm-4.4.2-e89b531279-ecc9f626fa.zip and /dev/null differ diff --git a/.yarn/cache/@babel-compat-data-npm-7.26.8-fb802c4940-1bb04c6860.zip b/.yarn/cache/@babel-compat-data-npm-7.26.8-fb802c4940-1bb04c6860.zip deleted file mode 100644 index 567c242b5..000000000 Binary files a/.yarn/cache/@babel-compat-data-npm-7.26.8-fb802c4940-1bb04c6860.zip and /dev/null differ diff --git a/.yarn/cache/@babel-core-npm-7.26.10-0b29e369b5-0217325bd4.zip b/.yarn/cache/@babel-core-npm-7.26.10-0b29e369b5-0217325bd4.zip deleted file mode 100644 index e23bec7c2..000000000 Binary files a/.yarn/cache/@babel-core-npm-7.26.10-0b29e369b5-0217325bd4.zip and /dev/null differ diff --git a/.yarn/cache/@babel-generator-npm-7.27.0-47f3db45ce-cdb6e3e844.zip b/.yarn/cache/@babel-generator-npm-7.27.0-47f3db45ce-cdb6e3e844.zip deleted file mode 100644 index d89b7d30a..000000000 Binary files a/.yarn/cache/@babel-generator-npm-7.27.0-47f3db45ce-cdb6e3e844.zip and /dev/null differ diff --git a/.yarn/cache/@babel-helper-compilation-targets-npm-7.27.0-a682051943-ad8b2351cd.zip b/.yarn/cache/@babel-helper-compilation-targets-npm-7.27.0-a682051943-ad8b2351cd.zip deleted file mode 100644 index 4e6f93096..000000000 Binary files a/.yarn/cache/@babel-helper-compilation-targets-npm-7.27.0-a682051943-ad8b2351cd.zip and /dev/null differ diff --git a/.yarn/cache/@babel-helper-plugin-utils-npm-7.26.5-f9c17c9880-4771fbb171.zip b/.yarn/cache/@babel-helper-plugin-utils-npm-7.26.5-f9c17c9880-4771fbb171.zip deleted file mode 100644 index c480b8267..000000000 Binary files a/.yarn/cache/@babel-helper-plugin-utils-npm-7.26.5-f9c17c9880-4771fbb171.zip and /dev/null differ diff --git a/.yarn/cache/@babel-helpers-npm-7.27.0-661e2983d3-d11bb8ada0.zip b/.yarn/cache/@babel-helpers-npm-7.27.0-661e2983d3-d11bb8ada0.zip deleted file mode 100644 index 0ff770785..000000000 Binary files a/.yarn/cache/@babel-helpers-npm-7.27.0-661e2983d3-d11bb8ada0.zip and /dev/null differ diff --git a/.yarn/cache/@babel-parser-npm-7.27.0-ab7dea7b75-062a4e6d51.zip b/.yarn/cache/@babel-parser-npm-7.27.0-ab7dea7b75-062a4e6d51.zip deleted file mode 100644 index c0d32ed3b..000000000 Binary files a/.yarn/cache/@babel-parser-npm-7.27.0-ab7dea7b75-062a4e6d51.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-async-generators-npm-7.8.4-d10cf993c9-7ed1c1d9b9.zip b/.yarn/cache/@babel-plugin-syntax-async-generators-npm-7.8.4-d10cf993c9-7ed1c1d9b9.zip deleted file mode 100644 index bc3c60f08..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-async-generators-npm-7.8.4-d10cf993c9-7ed1c1d9b9.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-bigint-npm-7.8.3-b05d971e6c-3a10849d83.zip b/.yarn/cache/@babel-plugin-syntax-bigint-npm-7.8.3-b05d971e6c-3a10849d83.zip deleted file mode 100644 index 0134ce90a..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-bigint-npm-7.8.3-b05d971e6c-3a10849d83.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-class-properties-npm-7.12.13-002ee9d930-24f34b196d.zip b/.yarn/cache/@babel-plugin-syntax-class-properties-npm-7.12.13-002ee9d930-24f34b196d.zip deleted file mode 100644 index 7bddd9a6f..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-class-properties-npm-7.12.13-002ee9d930-24f34b196d.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-class-static-block-npm-7.14.5-7bdd0ff1b3-3e80814b5b.zip b/.yarn/cache/@babel-plugin-syntax-class-static-block-npm-7.14.5-7bdd0ff1b3-3e80814b5b.zip deleted file mode 100644 index 025890a46..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-class-static-block-npm-7.14.5-7bdd0ff1b3-3e80814b5b.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-import-attributes-npm-7.26.0-7a281ed168-c122aa5771.zip b/.yarn/cache/@babel-plugin-syntax-import-attributes-npm-7.26.0-7a281ed168-c122aa5771.zip deleted file mode 100644 index c99dca181..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-import-attributes-npm-7.26.0-7a281ed168-c122aa5771.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-import-meta-npm-7.10.4-4a0a0158bc-166ac1125d.zip b/.yarn/cache/@babel-plugin-syntax-import-meta-npm-7.10.4-4a0a0158bc-166ac1125d.zip deleted file mode 100644 index cbe92234b..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-import-meta-npm-7.10.4-4a0a0158bc-166ac1125d.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-json-strings-npm-7.8.3-6dc7848179-bf5aea1f31.zip b/.yarn/cache/@babel-plugin-syntax-json-strings-npm-7.8.3-6dc7848179-bf5aea1f31.zip deleted file mode 100644 index 027e0bdcc..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-json-strings-npm-7.8.3-6dc7848179-bf5aea1f31.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-logical-assignment-operators-npm-7.10.4-72ae00fdf6-aff3357703.zip b/.yarn/cache/@babel-plugin-syntax-logical-assignment-operators-npm-7.10.4-72ae00fdf6-aff3357703.zip deleted file mode 100644 index ddbc188c5..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-logical-assignment-operators-npm-7.10.4-72ae00fdf6-aff3357703.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-nullish-coalescing-operator-npm-7.8.3-8a723173b5-87aca49189.zip b/.yarn/cache/@babel-plugin-syntax-nullish-coalescing-operator-npm-7.8.3-8a723173b5-87aca49189.zip deleted file mode 100644 index 91115bda0..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-nullish-coalescing-operator-npm-7.8.3-8a723173b5-87aca49189.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-numeric-separator-npm-7.10.4-81444be605-01ec5547bd.zip b/.yarn/cache/@babel-plugin-syntax-numeric-separator-npm-7.10.4-81444be605-01ec5547bd.zip deleted file mode 100644 index f541ce07b..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-numeric-separator-npm-7.10.4-81444be605-01ec5547bd.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-object-rest-spread-npm-7.8.3-60bd05b6ae-fddcf581a5.zip b/.yarn/cache/@babel-plugin-syntax-object-rest-spread-npm-7.8.3-60bd05b6ae-fddcf581a5.zip deleted file mode 100644 index 9ad98a0b2..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-object-rest-spread-npm-7.8.3-60bd05b6ae-fddcf581a5.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-optional-catch-binding-npm-7.8.3-ce337427d8-910d90e72b.zip b/.yarn/cache/@babel-plugin-syntax-optional-catch-binding-npm-7.8.3-ce337427d8-910d90e72b.zip deleted file mode 100644 index dbc1482ba..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-optional-catch-binding-npm-7.8.3-ce337427d8-910d90e72b.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-optional-chaining-npm-7.8.3-f3f3c79579-eef94d53a1.zip b/.yarn/cache/@babel-plugin-syntax-optional-chaining-npm-7.8.3-f3f3c79579-eef94d53a1.zip deleted file mode 100644 index 1a12bdbd7..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-optional-chaining-npm-7.8.3-f3f3c79579-eef94d53a1.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-private-property-in-object-npm-7.14.5-ee837fdbb2-b317174783.zip b/.yarn/cache/@babel-plugin-syntax-private-property-in-object-npm-7.14.5-ee837fdbb2-b317174783.zip deleted file mode 100644 index f4e180130..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-private-property-in-object-npm-7.14.5-ee837fdbb2-b317174783.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-top-level-await-npm-7.14.5-60a0a2e83b-bbd1a56b09.zip b/.yarn/cache/@babel-plugin-syntax-top-level-await-npm-7.14.5-60a0a2e83b-bbd1a56b09.zip deleted file mode 100644 index 041d0452f..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-top-level-await-npm-7.14.5-60a0a2e83b-bbd1a56b09.zip and /dev/null differ diff --git a/.yarn/cache/@babel-plugin-syntax-typescript-npm-7.25.9-5201e4ba77-0e9821e8ba.zip b/.yarn/cache/@babel-plugin-syntax-typescript-npm-7.25.9-5201e4ba77-0e9821e8ba.zip deleted file mode 100644 index a210abd42..000000000 Binary files a/.yarn/cache/@babel-plugin-syntax-typescript-npm-7.25.9-5201e4ba77-0e9821e8ba.zip and /dev/null differ diff --git a/.yarn/cache/@babel-template-npm-7.27.0-941c698259-46d6db4c20.zip b/.yarn/cache/@babel-template-npm-7.27.0-941c698259-46d6db4c20.zip deleted file mode 100644 index 29be7fc96..000000000 Binary files a/.yarn/cache/@babel-template-npm-7.27.0-941c698259-46d6db4c20.zip and /dev/null differ diff --git a/.yarn/cache/@babel-traverse-npm-7.27.0-e376c6d256-922d22aa91.zip b/.yarn/cache/@babel-traverse-npm-7.27.0-e376c6d256-922d22aa91.zip deleted file mode 100644 index 031e1bd88..000000000 Binary files a/.yarn/cache/@babel-traverse-npm-7.27.0-e376c6d256-922d22aa91.zip and /dev/null differ diff --git a/.yarn/cache/@babel-types-npm-7.27.0-4fc27759e5-59582019eb.zip b/.yarn/cache/@babel-types-npm-7.27.0-4fc27759e5-59582019eb.zip deleted file mode 100644 index 4c8a7b9e5..000000000 Binary files a/.yarn/cache/@babel-types-npm-7.27.0-4fc27759e5-59582019eb.zip and /dev/null differ diff --git a/.yarn/cache/@bcoe-v8-coverage-npm-0.2.3-9e27b3c57e-850f930553.zip b/.yarn/cache/@bcoe-v8-coverage-npm-0.2.3-9e27b3c57e-850f930553.zip deleted file mode 100644 index 8725d33ac..000000000 Binary files a/.yarn/cache/@bcoe-v8-coverage-npm-0.2.3-9e27b3c57e-850f930553.zip and /dev/null differ diff --git a/.yarn/cache/@cspotcode-source-map-support-npm-0.8.1-964f2de99d-5718f26708.zip b/.yarn/cache/@cspotcode-source-map-support-npm-0.8.1-964f2de99d-5718f26708.zip deleted file mode 100644 index 1a4763a4f..000000000 Binary files a/.yarn/cache/@cspotcode-source-map-support-npm-0.8.1-964f2de99d-5718f26708.zip and /dev/null differ diff --git a/.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-d578da5e2e.zip b/.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-d578da5e2e.zip deleted file mode 100644 index 3e663a24f..000000000 Binary files a/.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-d578da5e2e.zip and /dev/null differ diff --git a/.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-5282759d96.zip b/.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-5282759d96.zip deleted file mode 100644 index 5796f7601..000000000 Binary files a/.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-5282759d96.zip and /dev/null differ diff --git a/.yarn/cache/@jest-console-npm-29.7.0-77689f186f-0e3624e32c.zip b/.yarn/cache/@jest-console-npm-29.7.0-77689f186f-0e3624e32c.zip deleted file mode 100644 index 29c3e0176..000000000 Binary files a/.yarn/cache/@jest-console-npm-29.7.0-77689f186f-0e3624e32c.zip and /dev/null differ diff --git a/.yarn/cache/@jest-core-npm-29.7.0-cef60d74c4-af759c9781.zip b/.yarn/cache/@jest-core-npm-29.7.0-cef60d74c4-af759c9781.zip deleted file mode 100644 index ac1132240..000000000 Binary files a/.yarn/cache/@jest-core-npm-29.7.0-cef60d74c4-af759c9781.zip and /dev/null differ diff --git a/.yarn/cache/@jest-environment-npm-29.7.0-97705658d0-6fb398143b.zip b/.yarn/cache/@jest-environment-npm-29.7.0-97705658d0-6fb398143b.zip deleted file mode 100644 index 12768f5eb..000000000 Binary files a/.yarn/cache/@jest-environment-npm-29.7.0-97705658d0-6fb398143b.zip and /dev/null differ diff --git a/.yarn/cache/@jest-expect-npm-29.7.0-9dfe9cebaa-a01cb85fd9.zip b/.yarn/cache/@jest-expect-npm-29.7.0-9dfe9cebaa-a01cb85fd9.zip deleted file mode 100644 index 353593927..000000000 Binary files a/.yarn/cache/@jest-expect-npm-29.7.0-9dfe9cebaa-a01cb85fd9.zip and /dev/null differ diff --git a/.yarn/cache/@jest-expect-utils-npm-29.7.0-14740cc487-75eb177f3d.zip b/.yarn/cache/@jest-expect-utils-npm-29.7.0-14740cc487-75eb177f3d.zip deleted file mode 100644 index b104b3ed4..000000000 Binary files a/.yarn/cache/@jest-expect-utils-npm-29.7.0-14740cc487-75eb177f3d.zip and /dev/null differ diff --git a/.yarn/cache/@jest-fake-timers-npm-29.7.0-e4174d1b56-caf2bbd11f.zip b/.yarn/cache/@jest-fake-timers-npm-29.7.0-e4174d1b56-caf2bbd11f.zip deleted file mode 100644 index 45934cde9..000000000 Binary files a/.yarn/cache/@jest-fake-timers-npm-29.7.0-e4174d1b56-caf2bbd11f.zip and /dev/null differ diff --git a/.yarn/cache/@jest-globals-npm-29.7.0-06f2bd411e-97dbb94591.zip b/.yarn/cache/@jest-globals-npm-29.7.0-06f2bd411e-97dbb94591.zip deleted file mode 100644 index 23f3bac68..000000000 Binary files a/.yarn/cache/@jest-globals-npm-29.7.0-06f2bd411e-97dbb94591.zip and /dev/null differ diff --git a/.yarn/cache/@jest-reporters-npm-29.7.0-2561cd7a09-7eadabd62c.zip b/.yarn/cache/@jest-reporters-npm-29.7.0-2561cd7a09-7eadabd62c.zip deleted file mode 100644 index 6a993dd55..000000000 Binary files a/.yarn/cache/@jest-reporters-npm-29.7.0-2561cd7a09-7eadabd62c.zip and /dev/null differ diff --git a/.yarn/cache/@jest-schemas-npm-29.6.3-292730e442-910040425f.zip b/.yarn/cache/@jest-schemas-npm-29.6.3-292730e442-910040425f.zip deleted file mode 100644 index ce56da451..000000000 Binary files a/.yarn/cache/@jest-schemas-npm-29.6.3-292730e442-910040425f.zip and /dev/null differ diff --git a/.yarn/cache/@jest-source-map-npm-29.6.3-8bb8289263-bcc5a8697d.zip b/.yarn/cache/@jest-source-map-npm-29.6.3-8bb8289263-bcc5a8697d.zip deleted file mode 100644 index 57b5f024f..000000000 Binary files a/.yarn/cache/@jest-source-map-npm-29.6.3-8bb8289263-bcc5a8697d.zip and /dev/null differ diff --git a/.yarn/cache/@jest-test-result-npm-29.7.0-4bb532101b-67b6317d52.zip b/.yarn/cache/@jest-test-result-npm-29.7.0-4bb532101b-67b6317d52.zip deleted file mode 100644 index 59da95615..000000000 Binary files a/.yarn/cache/@jest-test-result-npm-29.7.0-4bb532101b-67b6317d52.zip and /dev/null differ diff --git a/.yarn/cache/@jest-test-sequencer-npm-29.7.0-291f23a495-73f4359901.zip b/.yarn/cache/@jest-test-sequencer-npm-29.7.0-291f23a495-73f4359901.zip deleted file mode 100644 index d199c9ee9..000000000 Binary files a/.yarn/cache/@jest-test-sequencer-npm-29.7.0-291f23a495-73f4359901.zip and /dev/null differ diff --git a/.yarn/cache/@jest-transform-npm-29.7.0-af20d68b57-0f8ac9f413.zip b/.yarn/cache/@jest-transform-npm-29.7.0-af20d68b57-0f8ac9f413.zip deleted file mode 100644 index 29db8e964..000000000 Binary files a/.yarn/cache/@jest-transform-npm-29.7.0-af20d68b57-0f8ac9f413.zip and /dev/null differ diff --git a/.yarn/cache/@jest-types-npm-29.6.3-a584ca999d-a0bcf15dbb.zip b/.yarn/cache/@jest-types-npm-29.6.3-a584ca999d-a0bcf15dbb.zip deleted file mode 100644 index 1075f4ac9..000000000 Binary files a/.yarn/cache/@jest-types-npm-29.6.3-a584ca999d-a0bcf15dbb.zip and /dev/null differ diff --git a/.yarn/cache/@jridgewell-trace-mapping-npm-0.3.9-91625cd7fb-d89597752f.zip b/.yarn/cache/@jridgewell-trace-mapping-npm-0.3.9-91625cd7fb-d89597752f.zip deleted file mode 100644 index 8bf87a916..000000000 Binary files a/.yarn/cache/@jridgewell-trace-mapping-npm-0.3.9-91625cd7fb-d89597752f.zip and /dev/null differ diff --git a/.yarn/cache/@sinclair-typebox-npm-0.27.8-23e206d653-00bd7362a3.zip b/.yarn/cache/@sinclair-typebox-npm-0.27.8-23e206d653-00bd7362a3.zip deleted file mode 100644 index 6576452af..000000000 Binary files a/.yarn/cache/@sinclair-typebox-npm-0.27.8-23e206d653-00bd7362a3.zip and /dev/null differ diff --git a/.yarn/cache/@sinonjs-commons-npm-3.0.1-bffb9f5a53-a7c3e7cc61.zip b/.yarn/cache/@sinonjs-commons-npm-3.0.1-bffb9f5a53-a7c3e7cc61.zip deleted file mode 100644 index 2b3482b1a..000000000 Binary files a/.yarn/cache/@sinonjs-commons-npm-3.0.1-bffb9f5a53-a7c3e7cc61.zip and /dev/null differ diff --git a/.yarn/cache/@sinonjs-fake-timers-npm-10.3.0-7417f876b4-614d30cb4d.zip b/.yarn/cache/@sinonjs-fake-timers-npm-10.3.0-7417f876b4-614d30cb4d.zip deleted file mode 100644 index 0f96731a6..000000000 Binary files a/.yarn/cache/@sinonjs-fake-timers-npm-10.3.0-7417f876b4-614d30cb4d.zip and /dev/null differ diff --git a/.yarn/cache/@testing-library-dom-npm-10.4.0-a0d2ca848e-bb128b90be.zip b/.yarn/cache/@testing-library-dom-npm-10.4.0-a0d2ca848e-bb128b90be.zip deleted file mode 100644 index b74f502e1..000000000 Binary files a/.yarn/cache/@testing-library-dom-npm-10.4.0-a0d2ca848e-bb128b90be.zip and /dev/null differ diff --git a/.yarn/cache/@testing-library-jest-dom-npm-6.6.3-733adae273-c1dc4260b0.zip b/.yarn/cache/@testing-library-jest-dom-npm-6.6.3-733adae273-c1dc4260b0.zip deleted file mode 100644 index 531d76686..000000000 Binary files a/.yarn/cache/@testing-library-jest-dom-npm-6.6.3-733adae273-c1dc4260b0.zip and /dev/null differ diff --git a/.yarn/cache/@testing-library-react-npm-16.3.0-fcde6d23a2-85728ea8a1.zip b/.yarn/cache/@testing-library-react-npm-16.3.0-fcde6d23a2-85728ea8a1.zip deleted file mode 100644 index 3f274e794..000000000 Binary files a/.yarn/cache/@testing-library-react-npm-16.3.0-fcde6d23a2-85728ea8a1.zip and /dev/null differ diff --git a/.yarn/cache/@testing-library-user-event-npm-14.6.1-5da7e1d4e2-4cb8a81fea.zip b/.yarn/cache/@testing-library-user-event-npm-14.6.1-5da7e1d4e2-4cb8a81fea.zip deleted file mode 100644 index de0ce76bb..000000000 Binary files a/.yarn/cache/@testing-library-user-event-npm-14.6.1-5da7e1d4e2-4cb8a81fea.zip and /dev/null differ diff --git a/.yarn/cache/@tootallnate-once-npm-2.0.0-e36cf4f140-ad87447820.zip b/.yarn/cache/@tootallnate-once-npm-2.0.0-e36cf4f140-ad87447820.zip deleted file mode 100644 index d240a82ae..000000000 Binary files a/.yarn/cache/@tootallnate-once-npm-2.0.0-e36cf4f140-ad87447820.zip and /dev/null differ diff --git a/.yarn/cache/@tsconfig-node10-npm-1.0.11-ab23db00e2-51fe47d55f.zip b/.yarn/cache/@tsconfig-node10-npm-1.0.11-ab23db00e2-51fe47d55f.zip deleted file mode 100644 index 2d94bd292..000000000 Binary files a/.yarn/cache/@tsconfig-node10-npm-1.0.11-ab23db00e2-51fe47d55f.zip and /dev/null differ diff --git a/.yarn/cache/@tsconfig-node12-npm-1.0.11-9710d1c61b-5ce29a41b1.zip b/.yarn/cache/@tsconfig-node12-npm-1.0.11-9710d1c61b-5ce29a41b1.zip deleted file mode 100644 index 001dd135c..000000000 Binary files a/.yarn/cache/@tsconfig-node12-npm-1.0.11-9710d1c61b-5ce29a41b1.zip and /dev/null differ diff --git a/.yarn/cache/@tsconfig-node14-npm-1.0.3-15321421d2-19275fe80c.zip b/.yarn/cache/@tsconfig-node14-npm-1.0.3-15321421d2-19275fe80c.zip deleted file mode 100644 index 9b825a0f1..000000000 Binary files a/.yarn/cache/@tsconfig-node14-npm-1.0.3-15321421d2-19275fe80c.zip and /dev/null differ diff --git a/.yarn/cache/@tsconfig-node16-npm-1.0.4-b7cb87d859-2023197859.zip b/.yarn/cache/@tsconfig-node16-npm-1.0.4-b7cb87d859-2023197859.zip deleted file mode 100644 index 2638f0fa8..000000000 Binary files a/.yarn/cache/@tsconfig-node16-npm-1.0.4-b7cb87d859-2023197859.zip and /dev/null differ diff --git a/.yarn/cache/@types-aria-query-npm-5.0.4-51d2b61619-ad8b87e4ad.zip b/.yarn/cache/@types-aria-query-npm-5.0.4-51d2b61619-ad8b87e4ad.zip deleted file mode 100644 index 80e9a2035..000000000 Binary files a/.yarn/cache/@types-aria-query-npm-5.0.4-51d2b61619-ad8b87e4ad.zip and /dev/null differ diff --git a/.yarn/cache/@types-babel__traverse-npm-7.20.7-06119f1d53-2a2e5ad29c.zip b/.yarn/cache/@types-babel__traverse-npm-7.20.7-06119f1d53-2a2e5ad29c.zip deleted file mode 100644 index 726dee06d..000000000 Binary files a/.yarn/cache/@types-babel__traverse-npm-7.20.7-06119f1d53-2a2e5ad29c.zip and /dev/null differ diff --git a/.yarn/cache/@types-graceful-fs-npm-4.1.9-ebd697fe83-79d746a8f0.zip b/.yarn/cache/@types-graceful-fs-npm-4.1.9-ebd697fe83-79d746a8f0.zip deleted file mode 100644 index 8af594bc6..000000000 Binary files a/.yarn/cache/@types-graceful-fs-npm-4.1.9-ebd697fe83-79d746a8f0.zip and /dev/null differ diff --git a/.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.6-2ea31fda9c-3feac423fd.zip b/.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.6-2ea31fda9c-3feac423fd.zip deleted file mode 100644 index c09edec14..000000000 Binary files a/.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.6-2ea31fda9c-3feac423fd.zip and /dev/null differ diff --git a/.yarn/cache/@types-istanbul-lib-report-npm-3.0.3-a5c0ef4b88-b91e9b60f8.zip b/.yarn/cache/@types-istanbul-lib-report-npm-3.0.3-a5c0ef4b88-b91e9b60f8.zip deleted file mode 100644 index b9934ced9..000000000 Binary files a/.yarn/cache/@types-istanbul-lib-report-npm-3.0.3-a5c0ef4b88-b91e9b60f8.zip and /dev/null differ diff --git a/.yarn/cache/@types-istanbul-reports-npm-3.0.4-1afa69db29-93eb188357.zip b/.yarn/cache/@types-istanbul-reports-npm-3.0.4-1afa69db29-93eb188357.zip deleted file mode 100644 index 47eedca94..000000000 Binary files a/.yarn/cache/@types-istanbul-reports-npm-3.0.4-1afa69db29-93eb188357.zip and /dev/null differ diff --git a/.yarn/cache/@types-jest-npm-29.5.14-506446c38e-18dba4623f.zip b/.yarn/cache/@types-jest-npm-29.5.14-506446c38e-18dba4623f.zip deleted file mode 100644 index be6d9766b..000000000 Binary files a/.yarn/cache/@types-jest-npm-29.5.14-506446c38e-18dba4623f.zip and /dev/null differ diff --git a/.yarn/cache/@types-jsdom-npm-20.0.1-5bb899e006-d55402c525.zip b/.yarn/cache/@types-jsdom-npm-20.0.1-5bb899e006-d55402c525.zip deleted file mode 100644 index 964a2fdc7..000000000 Binary files a/.yarn/cache/@types-jsdom-npm-20.0.1-5bb899e006-d55402c525.zip and /dev/null differ diff --git a/.yarn/cache/@types-mongodb-npm-4.0.7-143cb37ea3-10ebdfed5b.zip b/.yarn/cache/@types-mongodb-npm-4.0.7-143cb37ea3-10ebdfed5b.zip deleted file mode 100644 index d0eebf054..000000000 Binary files a/.yarn/cache/@types-mongodb-npm-4.0.7-143cb37ea3-10ebdfed5b.zip and /dev/null differ diff --git a/.yarn/cache/@types-react-npm-18.2.31-6e2d07ce27-b11be8e391.zip b/.yarn/cache/@types-react-npm-18.2.31-6e2d07ce27-b11be8e391.zip deleted file mode 100644 index 5c052d695..000000000 Binary files a/.yarn/cache/@types-react-npm-18.2.31-6e2d07ce27-b11be8e391.zip and /dev/null differ diff --git a/.yarn/cache/@types-stack-utils-npm-2.0.3-48a0a03262-72576cc152.zip b/.yarn/cache/@types-stack-utils-npm-2.0.3-48a0a03262-72576cc152.zip deleted file mode 100644 index 875101af5..000000000 Binary files a/.yarn/cache/@types-stack-utils-npm-2.0.3-48a0a03262-72576cc152.zip and /dev/null differ diff --git a/.yarn/cache/@types-tough-cookie-npm-4.0.5-8c5e2162e1-f19409d019.zip b/.yarn/cache/@types-tough-cookie-npm-4.0.5-8c5e2162e1-f19409d019.zip deleted file mode 100644 index 483f4a101..000000000 Binary files a/.yarn/cache/@types-tough-cookie-npm-4.0.5-8c5e2162e1-f19409d019.zip and /dev/null differ diff --git a/.yarn/cache/@types-yargs-npm-17.0.33-1d6cca6a2e-ee013f2574.zip b/.yarn/cache/@types-yargs-npm-17.0.33-1d6cca6a2e-ee013f2574.zip deleted file mode 100644 index 7eb6600b9..000000000 Binary files a/.yarn/cache/@types-yargs-npm-17.0.33-1d6cca6a2e-ee013f2574.zip and /dev/null differ diff --git a/.yarn/cache/abab-npm-2.0.6-2662fba7f0-6ffc1af4ff.zip b/.yarn/cache/abab-npm-2.0.6-2662fba7f0-6ffc1af4ff.zip deleted file mode 100644 index f671c303c..000000000 Binary files a/.yarn/cache/abab-npm-2.0.6-2662fba7f0-6ffc1af4ff.zip and /dev/null differ diff --git a/.yarn/cache/acorn-globals-npm-7.0.1-97c48c0140-2a2998a547.zip b/.yarn/cache/acorn-globals-npm-7.0.1-97c48c0140-2a2998a547.zip deleted file mode 100644 index f18c3d9ca..000000000 Binary files a/.yarn/cache/acorn-globals-npm-7.0.1-97c48c0140-2a2998a547.zip and /dev/null differ diff --git a/.yarn/cache/acorn-npm-8.14.1-aee76ee752-260d9bb601.zip b/.yarn/cache/acorn-npm-8.14.1-aee76ee752-260d9bb601.zip deleted file mode 100644 index 42bdfaa85..000000000 Binary files a/.yarn/cache/acorn-npm-8.14.1-aee76ee752-260d9bb601.zip and /dev/null differ diff --git a/.yarn/cache/acorn-walk-npm-8.3.4-a75fa85ead-4ff03f4232.zip b/.yarn/cache/acorn-walk-npm-8.3.4-a75fa85ead-4ff03f4232.zip deleted file mode 100644 index ff1cfce10..000000000 Binary files a/.yarn/cache/acorn-walk-npm-8.3.4-a75fa85ead-4ff03f4232.zip and /dev/null differ diff --git a/.yarn/cache/agent-base-npm-6.0.2-428f325a93-f52b6872cc.zip b/.yarn/cache/agent-base-npm-6.0.2-428f325a93-f52b6872cc.zip deleted file mode 100644 index c7d271af2..000000000 Binary files a/.yarn/cache/agent-base-npm-6.0.2-428f325a93-f52b6872cc.zip and /dev/null differ diff --git a/.yarn/cache/agent-base-npm-7.1.3-b2c16e72fb-87bb7ee54f.zip b/.yarn/cache/agent-base-npm-7.1.3-b2c16e72fb-87bb7ee54f.zip deleted file mode 100644 index 9a5291911..000000000 Binary files a/.yarn/cache/agent-base-npm-7.1.3-b2c16e72fb-87bb7ee54f.zip and /dev/null differ diff --git a/.yarn/cache/ansi-escapes-npm-4.3.2-3ad173702f-93111c4218.zip b/.yarn/cache/ansi-escapes-npm-4.3.2-3ad173702f-93111c4218.zip deleted file mode 100644 index 6b90effb5..000000000 Binary files a/.yarn/cache/ansi-escapes-npm-4.3.2-3ad173702f-93111c4218.zip and /dev/null differ diff --git a/.yarn/cache/ansi-styles-npm-5.2.0-72fc7003e3-d7f4e97ce0.zip b/.yarn/cache/ansi-styles-npm-5.2.0-72fc7003e3-d7f4e97ce0.zip deleted file mode 100644 index 62c09039b..000000000 Binary files a/.yarn/cache/ansi-styles-npm-5.2.0-72fc7003e3-d7f4e97ce0.zip and /dev/null differ diff --git a/.yarn/cache/arg-npm-4.1.3-1748b966a8-544af8dd3f.zip b/.yarn/cache/arg-npm-4.1.3-1748b966a8-544af8dd3f.zip deleted file mode 100644 index 21128e2b8..000000000 Binary files a/.yarn/cache/arg-npm-4.1.3-1748b966a8-544af8dd3f.zip and /dev/null differ diff --git a/.yarn/cache/aria-query-npm-5.3.0-76575ac83b-305bd73c76.zip b/.yarn/cache/aria-query-npm-5.3.0-76575ac83b-305bd73c76.zip deleted file mode 100644 index fe82cd9c1..000000000 Binary files a/.yarn/cache/aria-query-npm-5.3.0-76575ac83b-305bd73c76.zip and /dev/null differ diff --git a/.yarn/cache/async-mutex-npm-0.5.0-cc288ce63d-be1587f487.zip b/.yarn/cache/async-mutex-npm-0.5.0-cc288ce63d-be1587f487.zip deleted file mode 100644 index c8b7ef87a..000000000 Binary files a/.yarn/cache/async-mutex-npm-0.5.0-cc288ce63d-be1587f487.zip and /dev/null differ diff --git a/.yarn/cache/b4a-npm-1.6.7-a52d28b4e2-afe4e239b4.zip b/.yarn/cache/b4a-npm-1.6.7-a52d28b4e2-afe4e239b4.zip deleted file mode 100644 index a9527c269..000000000 Binary files a/.yarn/cache/b4a-npm-1.6.7-a52d28b4e2-afe4e239b4.zip and /dev/null differ diff --git a/.yarn/cache/babel-jest-npm-29.7.0-273152fbe9-ee6f8e0495.zip b/.yarn/cache/babel-jest-npm-29.7.0-273152fbe9-ee6f8e0495.zip deleted file mode 100644 index e5097b35d..000000000 Binary files a/.yarn/cache/babel-jest-npm-29.7.0-273152fbe9-ee6f8e0495.zip and /dev/null differ diff --git a/.yarn/cache/babel-plugin-istanbul-npm-6.1.1-df824055e4-cb4fd95738.zip b/.yarn/cache/babel-plugin-istanbul-npm-6.1.1-df824055e4-cb4fd95738.zip deleted file mode 100644 index 6577c6eb6..000000000 Binary files a/.yarn/cache/babel-plugin-istanbul-npm-6.1.1-df824055e4-cb4fd95738.zip and /dev/null differ diff --git a/.yarn/cache/babel-plugin-jest-hoist-npm-29.6.3-46120a3297-51250f2281.zip b/.yarn/cache/babel-plugin-jest-hoist-npm-29.6.3-46120a3297-51250f2281.zip deleted file mode 100644 index 605fd52c2..000000000 Binary files a/.yarn/cache/babel-plugin-jest-hoist-npm-29.6.3-46120a3297-51250f2281.zip and /dev/null differ diff --git a/.yarn/cache/babel-preset-current-node-syntax-npm-1.1.0-a3b84fe89f-9f93fac975.zip b/.yarn/cache/babel-preset-current-node-syntax-npm-1.1.0-a3b84fe89f-9f93fac975.zip deleted file mode 100644 index b85d04795..000000000 Binary files a/.yarn/cache/babel-preset-current-node-syntax-npm-1.1.0-a3b84fe89f-9f93fac975.zip and /dev/null differ diff --git a/.yarn/cache/babel-preset-jest-npm-29.6.3-44bf6eeda9-aa4ff2a8a7.zip b/.yarn/cache/babel-preset-jest-npm-29.6.3-44bf6eeda9-aa4ff2a8a7.zip deleted file mode 100644 index 9f46181e5..000000000 Binary files a/.yarn/cache/babel-preset-jest-npm-29.6.3-44bf6eeda9-aa4ff2a8a7.zip and /dev/null differ diff --git a/.yarn/cache/bare-events-npm-2.5.4-ff815c1e18-522a5401ca.zip b/.yarn/cache/bare-events-npm-2.5.4-ff815c1e18-522a5401ca.zip deleted file mode 100644 index 50001fe2a..000000000 Binary files a/.yarn/cache/bare-events-npm-2.5.4-ff815c1e18-522a5401ca.zip and /dev/null differ diff --git a/.yarn/cache/bser-npm-2.1.1-cc902055ce-9ba4dc58ce.zip b/.yarn/cache/bser-npm-2.1.1-cc902055ce-9ba4dc58ce.zip deleted file mode 100644 index e83ce2e43..000000000 Binary files a/.yarn/cache/bser-npm-2.1.1-cc902055ce-9ba4dc58ce.zip and /dev/null differ diff --git a/.yarn/cache/buffer-from-npm-1.1.2-03d2f20d7e-0448524a56.zip b/.yarn/cache/buffer-from-npm-1.1.2-03d2f20d7e-0448524a56.zip deleted file mode 100644 index efe1b7638..000000000 Binary files a/.yarn/cache/buffer-from-npm-1.1.2-03d2f20d7e-0448524a56.zip and /dev/null differ diff --git a/.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-e6effce26b.zip b/.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-e6effce26b.zip deleted file mode 100644 index 9cc2f6ddf..000000000 Binary files a/.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-e6effce26b.zip and /dev/null differ diff --git a/.yarn/cache/chalk-npm-3.0.0-e813208025-8e3ddf3981.zip b/.yarn/cache/chalk-npm-3.0.0-e813208025-8e3ddf3981.zip deleted file mode 100644 index 47b36c701..000000000 Binary files a/.yarn/cache/chalk-npm-3.0.0-e813208025-8e3ddf3981.zip and /dev/null differ diff --git a/.yarn/cache/char-regex-npm-1.0.2-ecade5f97f-b563e4b603.zip b/.yarn/cache/char-regex-npm-1.0.2-ecade5f97f-b563e4b603.zip deleted file mode 100644 index 208bdb8f9..000000000 Binary files a/.yarn/cache/char-regex-npm-1.0.2-ecade5f97f-b563e4b603.zip and /dev/null differ diff --git a/.yarn/cache/cjs-module-lexer-npm-1.4.3-4a46e7bf6c-221a1661a9.zip b/.yarn/cache/cjs-module-lexer-npm-1.4.3-4a46e7bf6c-221a1661a9.zip deleted file mode 100644 index fd54f1acb..000000000 Binary files a/.yarn/cache/cjs-module-lexer-npm-1.4.3-4a46e7bf6c-221a1661a9.zip and /dev/null differ diff --git a/.yarn/cache/cliui-npm-8.0.1-3b029092cf-79648b3b00.zip b/.yarn/cache/cliui-npm-8.0.1-3b029092cf-79648b3b00.zip deleted file mode 100644 index a90643c5e..000000000 Binary files a/.yarn/cache/cliui-npm-8.0.1-3b029092cf-79648b3b00.zip and /dev/null differ diff --git a/.yarn/cache/co-npm-4.6.0-03f2d1feb6-5210d92230.zip b/.yarn/cache/co-npm-4.6.0-03f2d1feb6-5210d92230.zip deleted file mode 100644 index be2bd8554..000000000 Binary files a/.yarn/cache/co-npm-4.6.0-03f2d1feb6-5210d92230.zip and /dev/null differ diff --git a/.yarn/cache/collect-v8-coverage-npm-1.0.2-bd20d0c572-c10f41c39a.zip b/.yarn/cache/collect-v8-coverage-npm-1.0.2-bd20d0c572-c10f41c39a.zip deleted file mode 100644 index 2d1512a36..000000000 Binary files a/.yarn/cache/collect-v8-coverage-npm-1.0.2-bd20d0c572-c10f41c39a.zip and /dev/null differ diff --git a/.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip b/.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip deleted file mode 100644 index b2b081748..000000000 Binary files a/.yarn/cache/commondir-npm-1.0.1-291b790340-59715f2fc4.zip and /dev/null differ diff --git a/.yarn/cache/create-jest-npm-29.7.0-3a6a7b993b-1427d49458.zip b/.yarn/cache/create-jest-npm-29.7.0-3a6a7b993b-1427d49458.zip deleted file mode 100644 index 393187bb4..000000000 Binary files a/.yarn/cache/create-jest-npm-29.7.0-3a6a7b993b-1427d49458.zip and /dev/null differ diff --git a/.yarn/cache/create-require-npm-1.1.1-839884ca2e-a9a1503d43.zip b/.yarn/cache/create-require-npm-1.1.1-839884ca2e-a9a1503d43.zip deleted file mode 100644 index afbfac210..000000000 Binary files a/.yarn/cache/create-require-npm-1.1.1-839884ca2e-a9a1503d43.zip and /dev/null differ diff --git a/.yarn/cache/css.escape-npm-1.5.1-b24d2ba77a-f6d38088d8.zip b/.yarn/cache/css.escape-npm-1.5.1-b24d2ba77a-f6d38088d8.zip deleted file mode 100644 index 036e6c29d..000000000 Binary files a/.yarn/cache/css.escape-npm-1.5.1-b24d2ba77a-f6d38088d8.zip and /dev/null differ diff --git a/.yarn/cache/cssom-npm-0.3.8-a9291d36ff-24beb3087c.zip b/.yarn/cache/cssom-npm-0.3.8-a9291d36ff-24beb3087c.zip deleted file mode 100644 index b204eba39..000000000 Binary files a/.yarn/cache/cssom-npm-0.3.8-a9291d36ff-24beb3087c.zip and /dev/null differ diff --git a/.yarn/cache/cssom-npm-0.5.0-44ab2704f2-823471aa30.zip b/.yarn/cache/cssom-npm-0.5.0-44ab2704f2-823471aa30.zip deleted file mode 100644 index dc47b2e75..000000000 Binary files a/.yarn/cache/cssom-npm-0.5.0-44ab2704f2-823471aa30.zip and /dev/null differ diff --git a/.yarn/cache/cssstyle-npm-2.3.0-b5d112c450-5f05e6fd2e.zip b/.yarn/cache/cssstyle-npm-2.3.0-b5d112c450-5f05e6fd2e.zip deleted file mode 100644 index 814eea726..000000000 Binary files a/.yarn/cache/cssstyle-npm-2.3.0-b5d112c450-5f05e6fd2e.zip and /dev/null differ diff --git a/.yarn/cache/data-urls-npm-3.0.2-c8b2050319-033fc3dd0f.zip b/.yarn/cache/data-urls-npm-3.0.2-c8b2050319-033fc3dd0f.zip deleted file mode 100644 index 015df7be6..000000000 Binary files a/.yarn/cache/data-urls-npm-3.0.2-c8b2050319-033fc3dd0f.zip and /dev/null differ diff --git a/.yarn/cache/debug-npm-4.4.0-f6efe76023-fb42df878d.zip b/.yarn/cache/debug-npm-4.4.0-f6efe76023-fb42df878d.zip deleted file mode 100644 index 9d9b13af5..000000000 Binary files a/.yarn/cache/debug-npm-4.4.0-f6efe76023-fb42df878d.zip and /dev/null differ diff --git a/.yarn/cache/decimal.js-npm-10.5.0-f021b10ac9-91c6b53b5d.zip b/.yarn/cache/decimal.js-npm-10.5.0-f021b10ac9-91c6b53b5d.zip deleted file mode 100644 index 58a11b392..000000000 Binary files a/.yarn/cache/decimal.js-npm-10.5.0-f021b10ac9-91c6b53b5d.zip and /dev/null differ diff --git a/.yarn/cache/dedent-npm-1.5.3-123726df15-045b595557.zip b/.yarn/cache/dedent-npm-1.5.3-123726df15-045b595557.zip deleted file mode 100644 index 05bbfa9b0..000000000 Binary files a/.yarn/cache/dedent-npm-1.5.3-123726df15-045b595557.zip and /dev/null differ diff --git a/.yarn/cache/detect-newline-npm-3.1.0-6d33fa8d37-ae6cd429c4.zip b/.yarn/cache/detect-newline-npm-3.1.0-6d33fa8d37-ae6cd429c4.zip deleted file mode 100644 index 95b9355c7..000000000 Binary files a/.yarn/cache/detect-newline-npm-3.1.0-6d33fa8d37-ae6cd429c4.zip and /dev/null differ diff --git a/.yarn/cache/diff-npm-4.0.2-73133c7102-f2c09b0ce4.zip b/.yarn/cache/diff-npm-4.0.2-73133c7102-f2c09b0ce4.zip deleted file mode 100644 index e532815fd..000000000 Binary files a/.yarn/cache/diff-npm-4.0.2-73133c7102-f2c09b0ce4.zip and /dev/null differ diff --git a/.yarn/cache/diff-sequences-npm-29.6.3-18ab2c9949-f4914158e1.zip b/.yarn/cache/diff-sequences-npm-29.6.3-18ab2c9949-f4914158e1.zip deleted file mode 100644 index 89803db13..000000000 Binary files a/.yarn/cache/diff-sequences-npm-29.6.3-18ab2c9949-f4914158e1.zip and /dev/null differ diff --git a/.yarn/cache/dom-accessibility-api-npm-0.5.16-d3e2310666-005eb283ca.zip b/.yarn/cache/dom-accessibility-api-npm-0.5.16-d3e2310666-005eb283ca.zip deleted file mode 100644 index 2d6509582..000000000 Binary files a/.yarn/cache/dom-accessibility-api-npm-0.5.16-d3e2310666-005eb283ca.zip and /dev/null differ diff --git a/.yarn/cache/dom-accessibility-api-npm-0.6.3-0345e4dede-c325b5144b.zip b/.yarn/cache/dom-accessibility-api-npm-0.6.3-0345e4dede-c325b5144b.zip deleted file mode 100644 index c87d1fc90..000000000 Binary files a/.yarn/cache/dom-accessibility-api-npm-0.6.3-0345e4dede-c325b5144b.zip and /dev/null differ diff --git a/.yarn/cache/domexception-npm-4.0.0-5093673f9b-ddbc1268ed.zip b/.yarn/cache/domexception-npm-4.0.0-5093673f9b-ddbc1268ed.zip deleted file mode 100644 index 4253de44a..000000000 Binary files a/.yarn/cache/domexception-npm-4.0.0-5093673f9b-ddbc1268ed.zip and /dev/null differ diff --git a/.yarn/cache/emittery-npm-0.13.1-cb6cd1bb03-2b089ab630.zip b/.yarn/cache/emittery-npm-0.13.1-cb6cd1bb03-2b089ab630.zip deleted file mode 100644 index e2a53f872..000000000 Binary files a/.yarn/cache/emittery-npm-0.13.1-cb6cd1bb03-2b089ab630.zip and /dev/null differ diff --git a/.yarn/cache/entities-npm-4.5.0-7cdb83b832-853f8ebd5b.zip b/.yarn/cache/entities-npm-4.5.0-7cdb83b832-853f8ebd5b.zip deleted file mode 100644 index 3772a4510..000000000 Binary files a/.yarn/cache/entities-npm-4.5.0-7cdb83b832-853f8ebd5b.zip and /dev/null differ diff --git a/.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-9f8a2d5743.zip b/.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-9f8a2d5743.zip deleted file mode 100644 index 5150d4e55..000000000 Binary files a/.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-9f8a2d5743.zip and /dev/null differ diff --git a/.yarn/cache/escodegen-npm-2.1.0-e0bf940745-096696407e.zip b/.yarn/cache/escodegen-npm-2.1.0-e0bf940745-096696407e.zip deleted file mode 100644 index d28acf846..000000000 Binary files a/.yarn/cache/escodegen-npm-2.1.0-e0bf940745-096696407e.zip and /dev/null differ diff --git a/.yarn/cache/exit-npm-0.1.2-ef3761a67d-abc407f07a.zip b/.yarn/cache/exit-npm-0.1.2-ef3761a67d-abc407f07a.zip deleted file mode 100644 index 87a2330e8..000000000 Binary files a/.yarn/cache/exit-npm-0.1.2-ef3761a67d-abc407f07a.zip and /dev/null differ diff --git a/.yarn/cache/expect-npm-29.7.0-62e9f7979e-9257f10288.zip b/.yarn/cache/expect-npm-29.7.0-62e9f7979e-9257f10288.zip deleted file mode 100644 index 4310bbcb1..000000000 Binary files a/.yarn/cache/expect-npm-29.7.0-62e9f7979e-9257f10288.zip and /dev/null differ diff --git a/.yarn/cache/fast-fifo-npm-1.3.2-391cc25df4-6bfcba3e4d.zip b/.yarn/cache/fast-fifo-npm-1.3.2-391cc25df4-6bfcba3e4d.zip deleted file mode 100644 index c99b59833..000000000 Binary files a/.yarn/cache/fast-fifo-npm-1.3.2-391cc25df4-6bfcba3e4d.zip and /dev/null differ diff --git a/.yarn/cache/fb-watchman-npm-2.0.2-bcb6f8f831-b15a124cef.zip b/.yarn/cache/fb-watchman-npm-2.0.2-bcb6f8f831-b15a124cef.zip deleted file mode 100644 index 63d51b050..000000000 Binary files a/.yarn/cache/fb-watchman-npm-2.0.2-bcb6f8f831-b15a124cef.zip and /dev/null differ diff --git a/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip b/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip deleted file mode 100644 index bb911f561..000000000 Binary files a/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-1e61c2e64f.zip and /dev/null differ diff --git a/.yarn/cache/get-caller-file-npm-2.0.5-80e8a86305-b9769a836d.zip b/.yarn/cache/get-caller-file-npm-2.0.5-80e8a86305-b9769a836d.zip deleted file mode 100644 index 0aa2c9cd0..000000000 Binary files a/.yarn/cache/get-caller-file-npm-2.0.5-80e8a86305-b9769a836d.zip and /dev/null differ diff --git a/.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-bba0811116.zip b/.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-bba0811116.zip deleted file mode 100644 index 3ea9023ca..000000000 Binary files a/.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-bba0811116.zip and /dev/null differ diff --git a/.yarn/cache/html-encoding-sniffer-npm-3.0.0-daac3dfe41-8d806aa004.zip b/.yarn/cache/html-encoding-sniffer-npm-3.0.0-daac3dfe41-8d806aa004.zip deleted file mode 100644 index 1fbdcc51d..000000000 Binary files a/.yarn/cache/html-encoding-sniffer-npm-3.0.0-daac3dfe41-8d806aa004.zip and /dev/null differ diff --git a/.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-d2df2da3ad.zip b/.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-d2df2da3ad.zip deleted file mode 100644 index cf5e7a077..000000000 Binary files a/.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-d2df2da3ad.zip and /dev/null differ diff --git a/.yarn/cache/http-proxy-agent-npm-5.0.0-7f1f121b83-e2ee1ff165.zip b/.yarn/cache/http-proxy-agent-npm-5.0.0-7f1f121b83-e2ee1ff165.zip deleted file mode 100644 index a999ab7d5..000000000 Binary files a/.yarn/cache/http-proxy-agent-npm-5.0.0-7f1f121b83-e2ee1ff165.zip and /dev/null differ diff --git a/.yarn/cache/https-proxy-agent-npm-5.0.1-42d65f358e-571fccdf38.zip b/.yarn/cache/https-proxy-agent-npm-5.0.1-42d65f358e-571fccdf38.zip deleted file mode 100644 index b8bc9949c..000000000 Binary files a/.yarn/cache/https-proxy-agent-npm-5.0.1-42d65f358e-571fccdf38.zip and /dev/null differ diff --git a/.yarn/cache/https-proxy-agent-npm-7.0.6-27a95c2690-b882377a12.zip b/.yarn/cache/https-proxy-agent-npm-7.0.6-27a95c2690-b882377a12.zip deleted file mode 100644 index 6fd64d49a..000000000 Binary files a/.yarn/cache/https-proxy-agent-npm-7.0.6-27a95c2690-b882377a12.zip and /dev/null differ diff --git a/.yarn/cache/import-local-npm-3.2.0-bf54ec7842-0b0b0b412b.zip b/.yarn/cache/import-local-npm-3.2.0-bf54ec7842-0b0b0b412b.zip deleted file mode 100644 index c9e4ee29c..000000000 Binary files a/.yarn/cache/import-local-npm-3.2.0-bf54ec7842-0b0b0b412b.zip and /dev/null differ diff --git a/.yarn/cache/is-core-module-npm-2.16.1-a54837229e-6ec5b3c42d.zip b/.yarn/cache/is-core-module-npm-2.16.1-a54837229e-6ec5b3c42d.zip deleted file mode 100644 index 24bb1f8cb..000000000 Binary files a/.yarn/cache/is-core-module-npm-2.16.1-a54837229e-6ec5b3c42d.zip and /dev/null differ diff --git a/.yarn/cache/is-generator-fn-npm-2.1.0-37895c2d2b-a6ad5492cf.zip b/.yarn/cache/is-generator-fn-npm-2.1.0-37895c2d2b-a6ad5492cf.zip deleted file mode 100644 index c9e807429..000000000 Binary files a/.yarn/cache/is-generator-fn-npm-2.1.0-37895c2d2b-a6ad5492cf.zip and /dev/null differ diff --git a/.yarn/cache/is-potential-custom-element-name-npm-1.0.1-f352f606f8-ced7bbbb64.zip b/.yarn/cache/is-potential-custom-element-name-npm-1.0.1-f352f606f8-ced7bbbb64.zip deleted file mode 100644 index ce2147e2a..000000000 Binary files a/.yarn/cache/is-potential-custom-element-name-npm-1.0.1-f352f606f8-ced7bbbb64.zip and /dev/null differ diff --git a/.yarn/cache/istanbul-lib-coverage-npm-3.2.2-5c0526e059-2367407a8d.zip b/.yarn/cache/istanbul-lib-coverage-npm-3.2.2-5c0526e059-2367407a8d.zip deleted file mode 100644 index e1256eff8..000000000 Binary files a/.yarn/cache/istanbul-lib-coverage-npm-3.2.2-5c0526e059-2367407a8d.zip and /dev/null differ diff --git a/.yarn/cache/istanbul-lib-instrument-npm-5.2.1-1b3ad719a9-bf16f1803b.zip b/.yarn/cache/istanbul-lib-instrument-npm-5.2.1-1b3ad719a9-bf16f1803b.zip deleted file mode 100644 index b630935eb..000000000 Binary files a/.yarn/cache/istanbul-lib-instrument-npm-5.2.1-1b3ad719a9-bf16f1803b.zip and /dev/null differ diff --git a/.yarn/cache/istanbul-lib-instrument-npm-6.0.3-959dca7404-74104c60c6.zip b/.yarn/cache/istanbul-lib-instrument-npm-6.0.3-959dca7404-74104c60c6.zip deleted file mode 100644 index 7bbd375b7..000000000 Binary files a/.yarn/cache/istanbul-lib-instrument-npm-6.0.3-959dca7404-74104c60c6.zip and /dev/null differ diff --git a/.yarn/cache/istanbul-lib-report-npm-3.0.1-b17446ab24-fd17a1b879.zip b/.yarn/cache/istanbul-lib-report-npm-3.0.1-b17446ab24-fd17a1b879.zip deleted file mode 100644 index b946848af..000000000 Binary files a/.yarn/cache/istanbul-lib-report-npm-3.0.1-b17446ab24-fd17a1b879.zip and /dev/null differ diff --git a/.yarn/cache/istanbul-lib-source-maps-npm-4.0.1-af0f859df7-21ad3df45d.zip b/.yarn/cache/istanbul-lib-source-maps-npm-4.0.1-af0f859df7-21ad3df45d.zip deleted file mode 100644 index 344cd7cdb..000000000 Binary files a/.yarn/cache/istanbul-lib-source-maps-npm-4.0.1-af0f859df7-21ad3df45d.zip and /dev/null differ diff --git a/.yarn/cache/istanbul-reports-npm-3.1.7-356486c0f4-2072db6e07.zip b/.yarn/cache/istanbul-reports-npm-3.1.7-356486c0f4-2072db6e07.zip deleted file mode 100644 index 39740c051..000000000 Binary files a/.yarn/cache/istanbul-reports-npm-3.1.7-356486c0f4-2072db6e07.zip and /dev/null differ diff --git a/.yarn/cache/jest-changed-files-npm-29.7.0-c2dcd10525-963e203893.zip b/.yarn/cache/jest-changed-files-npm-29.7.0-c2dcd10525-963e203893.zip deleted file mode 100644 index c0f4fb39a..000000000 Binary files a/.yarn/cache/jest-changed-files-npm-29.7.0-c2dcd10525-963e203893.zip and /dev/null differ diff --git a/.yarn/cache/jest-circus-npm-29.7.0-f7679858c6-3494371489.zip b/.yarn/cache/jest-circus-npm-29.7.0-f7679858c6-3494371489.zip deleted file mode 100644 index 1deda1c48..000000000 Binary files a/.yarn/cache/jest-circus-npm-29.7.0-f7679858c6-3494371489.zip and /dev/null differ diff --git a/.yarn/cache/jest-cli-npm-29.7.0-9adb356180-664901277a.zip b/.yarn/cache/jest-cli-npm-29.7.0-9adb356180-664901277a.zip deleted file mode 100644 index 3d5ca8101..000000000 Binary files a/.yarn/cache/jest-cli-npm-29.7.0-9adb356180-664901277a.zip and /dev/null differ diff --git a/.yarn/cache/jest-config-npm-29.7.0-97d8544d74-4cabf8f894.zip b/.yarn/cache/jest-config-npm-29.7.0-97d8544d74-4cabf8f894.zip deleted file mode 100644 index aa8d730c0..000000000 Binary files a/.yarn/cache/jest-config-npm-29.7.0-97d8544d74-4cabf8f894.zip and /dev/null differ diff --git a/.yarn/cache/jest-diff-npm-29.7.0-0149e01930-08e24a9dd4.zip b/.yarn/cache/jest-diff-npm-29.7.0-0149e01930-08e24a9dd4.zip deleted file mode 100644 index 13e779c9a..000000000 Binary files a/.yarn/cache/jest-diff-npm-29.7.0-0149e01930-08e24a9dd4.zip and /dev/null differ diff --git a/.yarn/cache/jest-docblock-npm-29.7.0-ec59f449dd-66390c3e94.zip b/.yarn/cache/jest-docblock-npm-29.7.0-ec59f449dd-66390c3e94.zip deleted file mode 100644 index c0da780a3..000000000 Binary files a/.yarn/cache/jest-docblock-npm-29.7.0-ec59f449dd-66390c3e94.zip and /dev/null differ diff --git a/.yarn/cache/jest-each-npm-29.7.0-93476f5ba0-e88f99f018.zip b/.yarn/cache/jest-each-npm-29.7.0-93476f5ba0-e88f99f018.zip deleted file mode 100644 index 3b4d9d93b..000000000 Binary files a/.yarn/cache/jest-each-npm-29.7.0-93476f5ba0-e88f99f018.zip and /dev/null differ diff --git a/.yarn/cache/jest-environment-jsdom-npm-29.7.0-0b72dd0e0b-559aac134c.zip b/.yarn/cache/jest-environment-jsdom-npm-29.7.0-0b72dd0e0b-559aac134c.zip deleted file mode 100644 index 5fc7f5b3c..000000000 Binary files a/.yarn/cache/jest-environment-jsdom-npm-29.7.0-0b72dd0e0b-559aac134c.zip and /dev/null differ diff --git a/.yarn/cache/jest-environment-node-npm-29.7.0-860b5e25ec-501a996629.zip b/.yarn/cache/jest-environment-node-npm-29.7.0-860b5e25ec-501a996629.zip deleted file mode 100644 index 622f32bd7..000000000 Binary files a/.yarn/cache/jest-environment-node-npm-29.7.0-860b5e25ec-501a996629.zip and /dev/null differ diff --git a/.yarn/cache/jest-get-type-npm-29.6.3-500477292e-88ac9102d4.zip b/.yarn/cache/jest-get-type-npm-29.6.3-500477292e-88ac9102d4.zip deleted file mode 100644 index 8afbbd1b3..000000000 Binary files a/.yarn/cache/jest-get-type-npm-29.6.3-500477292e-88ac9102d4.zip and /dev/null differ diff --git a/.yarn/cache/jest-haste-map-npm-29.7.0-e3be419eff-c2c8f2d3e7.zip b/.yarn/cache/jest-haste-map-npm-29.7.0-e3be419eff-c2c8f2d3e7.zip deleted file mode 100644 index f136b52bd..000000000 Binary files a/.yarn/cache/jest-haste-map-npm-29.7.0-e3be419eff-c2c8f2d3e7.zip and /dev/null differ diff --git a/.yarn/cache/jest-leak-detector-npm-29.7.0-915d82553f-e3950e3ddd.zip b/.yarn/cache/jest-leak-detector-npm-29.7.0-915d82553f-e3950e3ddd.zip deleted file mode 100644 index db3bcee1f..000000000 Binary files a/.yarn/cache/jest-leak-detector-npm-29.7.0-915d82553f-e3950e3ddd.zip and /dev/null differ diff --git a/.yarn/cache/jest-matcher-utils-npm-29.7.0-dfc74b630e-d7259e5f99.zip b/.yarn/cache/jest-matcher-utils-npm-29.7.0-dfc74b630e-d7259e5f99.zip deleted file mode 100644 index 25c776cf0..000000000 Binary files a/.yarn/cache/jest-matcher-utils-npm-29.7.0-dfc74b630e-d7259e5f99.zip and /dev/null differ diff --git a/.yarn/cache/jest-message-util-npm-29.7.0-7f88b6e8d1-a9d025b1c6.zip b/.yarn/cache/jest-message-util-npm-29.7.0-7f88b6e8d1-a9d025b1c6.zip deleted file mode 100644 index acdc44e09..000000000 Binary files a/.yarn/cache/jest-message-util-npm-29.7.0-7f88b6e8d1-a9d025b1c6.zip and /dev/null differ diff --git a/.yarn/cache/jest-mock-npm-29.7.0-22c4769d06-81ba9b6868.zip b/.yarn/cache/jest-mock-npm-29.7.0-22c4769d06-81ba9b6868.zip deleted file mode 100644 index b7e8baa47..000000000 Binary files a/.yarn/cache/jest-mock-npm-29.7.0-22c4769d06-81ba9b6868.zip and /dev/null differ diff --git a/.yarn/cache/jest-npm-29.7.0-d8dd095b81-17ca8d6750.zip b/.yarn/cache/jest-npm-29.7.0-d8dd095b81-17ca8d6750.zip deleted file mode 100644 index bd3db0ca2..000000000 Binary files a/.yarn/cache/jest-npm-29.7.0-d8dd095b81-17ca8d6750.zip and /dev/null differ diff --git a/.yarn/cache/jest-pnp-resolver-npm-1.2.3-70e06bf27c-db1a8ab2cb.zip b/.yarn/cache/jest-pnp-resolver-npm-1.2.3-70e06bf27c-db1a8ab2cb.zip deleted file mode 100644 index b4c4e5092..000000000 Binary files a/.yarn/cache/jest-pnp-resolver-npm-1.2.3-70e06bf27c-db1a8ab2cb.zip and /dev/null differ diff --git a/.yarn/cache/jest-regex-util-npm-29.6.3-568e0094e2-0518beeb9b.zip b/.yarn/cache/jest-regex-util-npm-29.6.3-568e0094e2-0518beeb9b.zip deleted file mode 100644 index ddf6af34e..000000000 Binary files a/.yarn/cache/jest-regex-util-npm-29.6.3-568e0094e2-0518beeb9b.zip and /dev/null differ diff --git a/.yarn/cache/jest-resolve-dependencies-npm-29.7.0-06ec582f1e-aeb75d8150.zip b/.yarn/cache/jest-resolve-dependencies-npm-29.7.0-06ec582f1e-aeb75d8150.zip deleted file mode 100644 index 00e4a3115..000000000 Binary files a/.yarn/cache/jest-resolve-dependencies-npm-29.7.0-06ec582f1e-aeb75d8150.zip and /dev/null differ diff --git a/.yarn/cache/jest-resolve-npm-29.7.0-5c36f0eefb-0ca218e107.zip b/.yarn/cache/jest-resolve-npm-29.7.0-5c36f0eefb-0ca218e107.zip deleted file mode 100644 index a72822fb0..000000000 Binary files a/.yarn/cache/jest-resolve-npm-29.7.0-5c36f0eefb-0ca218e107.zip and /dev/null differ diff --git a/.yarn/cache/jest-runner-npm-29.7.0-3bc9f82b58-f0405778ea.zip b/.yarn/cache/jest-runner-npm-29.7.0-3bc9f82b58-f0405778ea.zip deleted file mode 100644 index 50ad486f6..000000000 Binary files a/.yarn/cache/jest-runner-npm-29.7.0-3bc9f82b58-f0405778ea.zip and /dev/null differ diff --git a/.yarn/cache/jest-runtime-npm-29.7.0-120fa64128-d19f113d01.zip b/.yarn/cache/jest-runtime-npm-29.7.0-120fa64128-d19f113d01.zip deleted file mode 100644 index 4b50dc6a8..000000000 Binary files a/.yarn/cache/jest-runtime-npm-29.7.0-120fa64128-d19f113d01.zip and /dev/null differ diff --git a/.yarn/cache/jest-snapshot-npm-29.7.0-15ef0a4ad6-86821c3ad0.zip b/.yarn/cache/jest-snapshot-npm-29.7.0-15ef0a4ad6-86821c3ad0.zip deleted file mode 100644 index 2cf5f3982..000000000 Binary files a/.yarn/cache/jest-snapshot-npm-29.7.0-15ef0a4ad6-86821c3ad0.zip and /dev/null differ diff --git a/.yarn/cache/jest-util-npm-29.7.0-ff1d59714b-042ab4980f.zip b/.yarn/cache/jest-util-npm-29.7.0-ff1d59714b-042ab4980f.zip deleted file mode 100644 index 4ed8c18ce..000000000 Binary files a/.yarn/cache/jest-util-npm-29.7.0-ff1d59714b-042ab4980f.zip and /dev/null differ diff --git a/.yarn/cache/jest-validate-npm-29.7.0-795ac5ede8-191fcdc980.zip b/.yarn/cache/jest-validate-npm-29.7.0-795ac5ede8-191fcdc980.zip deleted file mode 100644 index b72af69cf..000000000 Binary files a/.yarn/cache/jest-validate-npm-29.7.0-795ac5ede8-191fcdc980.zip and /dev/null differ diff --git a/.yarn/cache/jest-watcher-npm-29.7.0-e5372f1629-67e6e7fe69.zip b/.yarn/cache/jest-watcher-npm-29.7.0-e5372f1629-67e6e7fe69.zip deleted file mode 100644 index 0aadb1792..000000000 Binary files a/.yarn/cache/jest-watcher-npm-29.7.0-e5372f1629-67e6e7fe69.zip and /dev/null differ diff --git a/.yarn/cache/jest-worker-npm-29.7.0-4d3567fed6-30fff60af4.zip b/.yarn/cache/jest-worker-npm-29.7.0-4d3567fed6-30fff60af4.zip deleted file mode 100644 index dbd140762..000000000 Binary files a/.yarn/cache/jest-worker-npm-29.7.0-4d3567fed6-30fff60af4.zip and /dev/null differ diff --git a/.yarn/cache/jsdom-npm-20.0.3-906a2f7005-6e2ae21db3.zip b/.yarn/cache/jsdom-npm-20.0.3-906a2f7005-6e2ae21db3.zip deleted file mode 100644 index af675bfee..000000000 Binary files a/.yarn/cache/jsdom-npm-20.0.3-906a2f7005-6e2ae21db3.zip and /dev/null differ diff --git a/.yarn/cache/leven-npm-3.1.0-b7697736a3-638401d534.zip b/.yarn/cache/leven-npm-3.1.0-b7697736a3-638401d534.zip deleted file mode 100644 index 227800ee0..000000000 Binary files a/.yarn/cache/leven-npm-3.1.0-b7697736a3-638401d534.zip and /dev/null differ diff --git a/.yarn/cache/lz-string-npm-1.5.0-3860794e30-1ee98b4580.zip b/.yarn/cache/lz-string-npm-1.5.0-3860794e30-1ee98b4580.zip deleted file mode 100644 index 2ef527c2d..000000000 Binary files a/.yarn/cache/lz-string-npm-1.5.0-3860794e30-1ee98b4580.zip and /dev/null differ diff --git a/.yarn/cache/make-dir-npm-3.1.0-d1d7505142-484200020a.zip b/.yarn/cache/make-dir-npm-3.1.0-d1d7505142-484200020a.zip deleted file mode 100644 index e466cd8a1..000000000 Binary files a/.yarn/cache/make-dir-npm-3.1.0-d1d7505142-484200020a.zip and /dev/null differ diff --git a/.yarn/cache/make-dir-npm-4.0.0-ec3cd921cc-bf0731a2dd.zip b/.yarn/cache/make-dir-npm-4.0.0-ec3cd921cc-bf0731a2dd.zip deleted file mode 100644 index 2a141eff6..000000000 Binary files a/.yarn/cache/make-dir-npm-4.0.0-ec3cd921cc-bf0731a2dd.zip and /dev/null differ diff --git a/.yarn/cache/makeerror-npm-1.0.12-69abf085d7-b38a025a12.zip b/.yarn/cache/makeerror-npm-1.0.12-69abf085d7-b38a025a12.zip deleted file mode 100644 index 8e32e3aa9..000000000 Binary files a/.yarn/cache/makeerror-npm-1.0.12-69abf085d7-b38a025a12.zip and /dev/null differ diff --git a/.yarn/cache/min-indent-npm-1.0.1-77031f50e1-bfc6dd03c5.zip b/.yarn/cache/min-indent-npm-1.0.1-77031f50e1-bfc6dd03c5.zip deleted file mode 100644 index 5ab689d40..000000000 Binary files a/.yarn/cache/min-indent-npm-1.0.1-77031f50e1-bfc6dd03c5.zip and /dev/null differ diff --git a/.yarn/cache/mongodb-memory-server-core-npm-10.1.4-85bdf3f8a0-a02fc2678c.zip b/.yarn/cache/mongodb-memory-server-core-npm-10.1.4-85bdf3f8a0-a02fc2678c.zip deleted file mode 100644 index 998d2763a..000000000 Binary files a/.yarn/cache/mongodb-memory-server-core-npm-10.1.4-85bdf3f8a0-a02fc2678c.zip and /dev/null differ diff --git a/.yarn/cache/mongodb-memory-server-npm-10.1.4-5c7ce446ca-8c9160f62d.zip b/.yarn/cache/mongodb-memory-server-npm-10.1.4-5c7ce446ca-8c9160f62d.zip deleted file mode 100644 index 85406c23a..000000000 Binary files a/.yarn/cache/mongodb-memory-server-npm-10.1.4-5c7ce446ca-8c9160f62d.zip and /dev/null differ diff --git a/.yarn/cache/mongodb-npm-6.15.0-94b2654a54-2572d10d47.zip b/.yarn/cache/mongodb-npm-6.15.0-94b2654a54-2572d10d47.zip deleted file mode 100644 index 2a5f1326d..000000000 Binary files a/.yarn/cache/mongodb-npm-6.15.0-94b2654a54-2572d10d47.zip and /dev/null differ diff --git a/.yarn/cache/new-find-package-json-npm-2.0.0-90004ee195-5488ead794.zip b/.yarn/cache/new-find-package-json-npm-2.0.0-90004ee195-5488ead794.zip deleted file mode 100644 index 1bb60abed..000000000 Binary files a/.yarn/cache/new-find-package-json-npm-2.0.0-90004ee195-5488ead794.zip and /dev/null differ diff --git a/.yarn/cache/node-int64-npm-0.4.0-0dc04ec3b2-d0b30b1ee6.zip b/.yarn/cache/node-int64-npm-0.4.0-0dc04ec3b2-d0b30b1ee6.zip deleted file mode 100644 index 6c6f6b273..000000000 Binary files a/.yarn/cache/node-int64-npm-0.4.0-0dc04ec3b2-d0b30b1ee6.zip and /dev/null differ diff --git a/.yarn/cache/nwsapi-npm-2.2.20-276b791049-37100d6023.zip b/.yarn/cache/nwsapi-npm-2.2.20-276b791049-37100d6023.zip deleted file mode 100644 index 82a84ecc2..000000000 Binary files a/.yarn/cache/nwsapi-npm-2.2.20-276b791049-37100d6023.zip and /dev/null differ diff --git a/.yarn/cache/parse5-npm-7.2.1-c48f333f28-11253cf8aa.zip b/.yarn/cache/parse5-npm-7.2.1-c48f333f28-11253cf8aa.zip deleted file mode 100644 index d751f80f9..000000000 Binary files a/.yarn/cache/parse5-npm-7.2.1-c48f333f28-11253cf8aa.zip and /dev/null differ diff --git a/.yarn/cache/pend-npm-1.2.0-7a13d93266-6c72f52433.zip b/.yarn/cache/pend-npm-1.2.0-7a13d93266-6c72f52433.zip deleted file mode 100644 index 03b6b6dec..000000000 Binary files a/.yarn/cache/pend-npm-1.2.0-7a13d93266-6c72f52433.zip and /dev/null differ diff --git a/.yarn/cache/pirates-npm-4.0.7-5e4ee2f078-3dcbaff13c.zip b/.yarn/cache/pirates-npm-4.0.7-5e4ee2f078-3dcbaff13c.zip deleted file mode 100644 index 13eeb3927..000000000 Binary files a/.yarn/cache/pirates-npm-4.0.7-5e4ee2f078-3dcbaff13c.zip and /dev/null differ diff --git a/.yarn/cache/pretty-format-npm-27.5.1-cd7d49696f-cf610cffcb.zip b/.yarn/cache/pretty-format-npm-27.5.1-cd7d49696f-cf610cffcb.zip deleted file mode 100644 index 8d28efe3e..000000000 Binary files a/.yarn/cache/pretty-format-npm-27.5.1-cd7d49696f-cf610cffcb.zip and /dev/null differ diff --git a/.yarn/cache/pretty-format-npm-29.7.0-7d330b2ea2-032c160238.zip b/.yarn/cache/pretty-format-npm-29.7.0-7d330b2ea2-032c160238.zip deleted file mode 100644 index 329581e27..000000000 Binary files a/.yarn/cache/pretty-format-npm-29.7.0-7d330b2ea2-032c160238.zip and /dev/null differ diff --git a/.yarn/cache/psl-npm-1.15.0-410584ca6b-6f777d82ee.zip b/.yarn/cache/psl-npm-1.15.0-410584ca6b-6f777d82ee.zip deleted file mode 100644 index eb58d25c6..000000000 Binary files a/.yarn/cache/psl-npm-1.15.0-410584ca6b-6f777d82ee.zip and /dev/null differ diff --git a/.yarn/cache/pure-rand-npm-6.1.0-497ea3fc37-8d53bc02be.zip b/.yarn/cache/pure-rand-npm-6.1.0-497ea3fc37-8d53bc02be.zip deleted file mode 100644 index a460d6577..000000000 Binary files a/.yarn/cache/pure-rand-npm-6.1.0-497ea3fc37-8d53bc02be.zip and /dev/null differ diff --git a/.yarn/cache/react-is-npm-17.0.2-091bbb8db6-9d6d111d89.zip b/.yarn/cache/react-is-npm-17.0.2-091bbb8db6-9d6d111d89.zip deleted file mode 100644 index 8b0c3e546..000000000 Binary files a/.yarn/cache/react-is-npm-17.0.2-091bbb8db6-9d6d111d89.zip and /dev/null differ diff --git a/.yarn/cache/redent-npm-3.0.0-31892f4906-fa1ef20404.zip b/.yarn/cache/redent-npm-3.0.0-31892f4906-fa1ef20404.zip deleted file mode 100644 index f0b77dfb5..000000000 Binary files a/.yarn/cache/redent-npm-3.0.0-31892f4906-fa1ef20404.zip and /dev/null differ diff --git a/.yarn/cache/require-directory-npm-2.1.1-8608aee50b-fb47e70bf0.zip b/.yarn/cache/require-directory-npm-2.1.1-8608aee50b-fb47e70bf0.zip deleted file mode 100644 index 5af5579b1..000000000 Binary files a/.yarn/cache/require-directory-npm-2.1.1-8608aee50b-fb47e70bf0.zip and /dev/null differ diff --git a/.yarn/cache/requires-port-npm-1.0.0-fd036b488a-eee0e303ad.zip b/.yarn/cache/requires-port-npm-1.0.0-fd036b488a-eee0e303ad.zip deleted file mode 100644 index b130302a5..000000000 Binary files a/.yarn/cache/requires-port-npm-1.0.0-fd036b488a-eee0e303ad.zip and /dev/null differ diff --git a/.yarn/cache/resolve-cwd-npm-3.0.0-e6f4e296bf-546e081601.zip b/.yarn/cache/resolve-cwd-npm-3.0.0-e6f4e296bf-546e081601.zip deleted file mode 100644 index d629f2246..000000000 Binary files a/.yarn/cache/resolve-cwd-npm-3.0.0-e6f4e296bf-546e081601.zip and /dev/null differ diff --git a/.yarn/cache/resolve-npm-1.22.10-d6fd9cdec7-ab7a32ff40.zip b/.yarn/cache/resolve-npm-1.22.10-d6fd9cdec7-ab7a32ff40.zip deleted file mode 100644 index e9230095b..000000000 Binary files a/.yarn/cache/resolve-npm-1.22.10-d6fd9cdec7-ab7a32ff40.zip and /dev/null differ diff --git a/.yarn/cache/resolve-patch-9fba488c5d-8aac1e4e46.zip b/.yarn/cache/resolve-patch-9fba488c5d-8aac1e4e46.zip deleted file mode 100644 index 227abf613..000000000 Binary files a/.yarn/cache/resolve-patch-9fba488c5d-8aac1e4e46.zip and /dev/null differ diff --git a/.yarn/cache/resolve.exports-npm-2.0.3-eb33ea72e9-abfb9f9827.zip b/.yarn/cache/resolve.exports-npm-2.0.3-eb33ea72e9-abfb9f9827.zip deleted file mode 100644 index a7fd07e37..000000000 Binary files a/.yarn/cache/resolve.exports-npm-2.0.3-eb33ea72e9-abfb9f9827.zip and /dev/null differ diff --git a/.yarn/cache/saxes-npm-6.0.0-31558949f5-d3fa3e2aaf.zip b/.yarn/cache/saxes-npm-6.0.0-31558949f5-d3fa3e2aaf.zip deleted file mode 100644 index 487af08ad..000000000 Binary files a/.yarn/cache/saxes-npm-6.0.0-31558949f5-d3fa3e2aaf.zip and /dev/null differ diff --git a/.yarn/cache/semver-npm-7.7.1-4572475307-586b825d36.zip b/.yarn/cache/semver-npm-7.7.1-4572475307-586b825d36.zip deleted file mode 100644 index e264f9596..000000000 Binary files a/.yarn/cache/semver-npm-7.7.1-4572475307-586b825d36.zip and /dev/null differ diff --git a/.yarn/cache/source-map-support-npm-0.5.13-377dfd7321-933550047b.zip b/.yarn/cache/source-map-support-npm-0.5.13-377dfd7321-933550047b.zip deleted file mode 100644 index 4fbf1b13a..000000000 Binary files a/.yarn/cache/source-map-support-npm-0.5.13-377dfd7321-933550047b.zip and /dev/null differ diff --git a/.yarn/cache/stack-utils-npm-2.0.6-2be1099696-052bf4d25b.zip b/.yarn/cache/stack-utils-npm-2.0.6-2be1099696-052bf4d25b.zip deleted file mode 100644 index df68e7d2e..000000000 Binary files a/.yarn/cache/stack-utils-npm-2.0.6-2be1099696-052bf4d25b.zip and /dev/null differ diff --git a/.yarn/cache/streamx-npm-2.22.0-6953aefe6d-9b2772a084.zip b/.yarn/cache/streamx-npm-2.22.0-6953aefe6d-9b2772a084.zip deleted file mode 100644 index 15c4198b4..000000000 Binary files a/.yarn/cache/streamx-npm-2.22.0-6953aefe6d-9b2772a084.zip and /dev/null differ diff --git a/.yarn/cache/string-length-npm-4.0.2-675173c7a2-ce85533ef5.zip b/.yarn/cache/string-length-npm-4.0.2-675173c7a2-ce85533ef5.zip deleted file mode 100644 index fd9f62fc8..000000000 Binary files a/.yarn/cache/string-length-npm-4.0.2-675173c7a2-ce85533ef5.zip and /dev/null differ diff --git a/.yarn/cache/strip-indent-npm-3.0.0-519e75a28d-18f045d57d.zip b/.yarn/cache/strip-indent-npm-3.0.0-519e75a28d-18f045d57d.zip deleted file mode 100644 index d24c48484..000000000 Binary files a/.yarn/cache/strip-indent-npm-3.0.0-519e75a28d-18f045d57d.zip and /dev/null differ diff --git a/.yarn/cache/supports-color-npm-8.1.1-289e937149-c052193a7e.zip b/.yarn/cache/supports-color-npm-8.1.1-289e937149-c052193a7e.zip deleted file mode 100644 index 3fd0d6c6a..000000000 Binary files a/.yarn/cache/supports-color-npm-8.1.1-289e937149-c052193a7e.zip and /dev/null differ diff --git a/.yarn/cache/symbol-tree-npm-3.2.4-fe70cdb75b-6e8fc7e148.zip b/.yarn/cache/symbol-tree-npm-3.2.4-fe70cdb75b-6e8fc7e148.zip deleted file mode 100644 index a52eafae7..000000000 Binary files a/.yarn/cache/symbol-tree-npm-3.2.4-fe70cdb75b-6e8fc7e148.zip and /dev/null differ diff --git a/.yarn/cache/tar-stream-npm-3.1.7-c34f9aa00f-6393a6c190.zip b/.yarn/cache/tar-stream-npm-3.1.7-c34f9aa00f-6393a6c190.zip deleted file mode 100644 index 72c8f106b..000000000 Binary files a/.yarn/cache/tar-stream-npm-3.1.7-c34f9aa00f-6393a6c190.zip and /dev/null differ diff --git a/.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-3b34a3d771.zip b/.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-3b34a3d771.zip deleted file mode 100644 index 00b9c4c04..000000000 Binary files a/.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-3b34a3d771.zip and /dev/null differ diff --git a/.yarn/cache/text-decoder-npm-1.2.3-cc7432569a-d7642a61f9.zip b/.yarn/cache/text-decoder-npm-1.2.3-cc7432569a-d7642a61f9.zip deleted file mode 100644 index bc637ac59..000000000 Binary files a/.yarn/cache/text-decoder-npm-1.2.3-cc7432569a-d7642a61f9.zip and /dev/null differ diff --git a/.yarn/cache/tmpl-npm-1.0.5-d399ba37e2-cd922d9b85.zip b/.yarn/cache/tmpl-npm-1.0.5-d399ba37e2-cd922d9b85.zip deleted file mode 100644 index f5bc8cda8..000000000 Binary files a/.yarn/cache/tmpl-npm-1.0.5-d399ba37e2-cd922d9b85.zip and /dev/null differ diff --git a/.yarn/cache/tough-cookie-npm-4.1.4-8293cc8bd5-5815059f01.zip b/.yarn/cache/tough-cookie-npm-4.1.4-8293cc8bd5-5815059f01.zip deleted file mode 100644 index 7f5a89f56..000000000 Binary files a/.yarn/cache/tough-cookie-npm-4.1.4-8293cc8bd5-5815059f01.zip and /dev/null differ diff --git a/.yarn/cache/ts-node-npm-10.9.2-3f3890b9ac-fde256c907.zip b/.yarn/cache/ts-node-npm-10.9.2-3f3890b9ac-fde256c907.zip deleted file mode 100644 index 437dc2b18..000000000 Binary files a/.yarn/cache/ts-node-npm-10.9.2-3f3890b9ac-fde256c907.zip and /dev/null differ diff --git a/.yarn/cache/type-detect-npm-4.0.8-8d8127b901-62b5628bff.zip b/.yarn/cache/type-detect-npm-4.0.8-8d8127b901-62b5628bff.zip deleted file mode 100644 index a3c01d86a..000000000 Binary files a/.yarn/cache/type-detect-npm-4.0.8-8d8127b901-62b5628bff.zip and /dev/null differ diff --git a/.yarn/cache/type-fest-npm-0.21.3-5ff2a9c6fd-e6b32a3b38.zip b/.yarn/cache/type-fest-npm-0.21.3-5ff2a9c6fd-e6b32a3b38.zip deleted file mode 100644 index 89f3fd57a..000000000 Binary files a/.yarn/cache/type-fest-npm-0.21.3-5ff2a9c6fd-e6b32a3b38.zip and /dev/null differ diff --git a/.yarn/cache/universalify-npm-0.2.0-9984e61c10-e86134cb12.zip b/.yarn/cache/universalify-npm-0.2.0-9984e61c10-e86134cb12.zip deleted file mode 100644 index 7f1d80c19..000000000 Binary files a/.yarn/cache/universalify-npm-0.2.0-9984e61c10-e86134cb12.zip and /dev/null differ diff --git a/.yarn/cache/url-parse-npm-1.5.10-64fa2bcd6d-fbdba6b1d8.zip b/.yarn/cache/url-parse-npm-1.5.10-64fa2bcd6d-fbdba6b1d8.zip deleted file mode 100644 index a0666e4f8..000000000 Binary files a/.yarn/cache/url-parse-npm-1.5.10-64fa2bcd6d-fbdba6b1d8.zip and /dev/null differ diff --git a/.yarn/cache/v8-compile-cache-lib-npm-3.0.1-4886071ece-78089ad549.zip b/.yarn/cache/v8-compile-cache-lib-npm-3.0.1-4886071ece-78089ad549.zip deleted file mode 100644 index 005ea7211..000000000 Binary files a/.yarn/cache/v8-compile-cache-lib-npm-3.0.1-4886071ece-78089ad549.zip and /dev/null differ diff --git a/.yarn/cache/v8-to-istanbul-npm-9.3.0-35fef658c9-ded42cd535.zip b/.yarn/cache/v8-to-istanbul-npm-9.3.0-35fef658c9-ded42cd535.zip deleted file mode 100644 index c279550d0..000000000 Binary files a/.yarn/cache/v8-to-istanbul-npm-9.3.0-35fef658c9-ded42cd535.zip and /dev/null differ diff --git a/.yarn/cache/w3c-xmlserializer-npm-4.0.0-f09d0ec3fc-eba070e78d.zip b/.yarn/cache/w3c-xmlserializer-npm-4.0.0-f09d0ec3fc-eba070e78d.zip deleted file mode 100644 index ae61bb6f0..000000000 Binary files a/.yarn/cache/w3c-xmlserializer-npm-4.0.0-f09d0ec3fc-eba070e78d.zip and /dev/null differ diff --git a/.yarn/cache/walker-npm-1.0.8-b0a05b9478-ad7a257ea1.zip b/.yarn/cache/walker-npm-1.0.8-b0a05b9478-ad7a257ea1.zip deleted file mode 100644 index 86c166881..000000000 Binary files a/.yarn/cache/walker-npm-1.0.8-b0a05b9478-ad7a257ea1.zip and /dev/null differ diff --git a/.yarn/cache/whatwg-encoding-npm-2.0.0-d7451f51b4-7087810c41.zip b/.yarn/cache/whatwg-encoding-npm-2.0.0-d7451f51b4-7087810c41.zip deleted file mode 100644 index 182445510..000000000 Binary files a/.yarn/cache/whatwg-encoding-npm-2.0.0-d7451f51b4-7087810c41.zip and /dev/null differ diff --git a/.yarn/cache/whatwg-mimetype-npm-3.0.0-5b617710c1-ce08bbb36b.zip b/.yarn/cache/whatwg-mimetype-npm-3.0.0-5b617710c1-ce08bbb36b.zip deleted file mode 100644 index aa91c250d..000000000 Binary files a/.yarn/cache/whatwg-mimetype-npm-3.0.0-5b617710c1-ce08bbb36b.zip and /dev/null differ diff --git a/.yarn/cache/write-file-atomic-npm-4.0.2-661baae4aa-5da60bd4ee.zip b/.yarn/cache/write-file-atomic-npm-4.0.2-661baae4aa-5da60bd4ee.zip deleted file mode 100644 index 127e30c69..000000000 Binary files a/.yarn/cache/write-file-atomic-npm-4.0.2-661baae4aa-5da60bd4ee.zip and /dev/null differ diff --git a/.yarn/cache/ws-npm-8.18.1-369b0f5491-4658357185.zip b/.yarn/cache/ws-npm-8.18.1-369b0f5491-4658357185.zip deleted file mode 100644 index a00d90247..000000000 Binary files a/.yarn/cache/ws-npm-8.18.1-369b0f5491-4658357185.zip and /dev/null differ diff --git a/.yarn/cache/xml-name-validator-npm-4.0.0-0857c21729-af100b79c2.zip b/.yarn/cache/xml-name-validator-npm-4.0.0-0857c21729-af100b79c2.zip deleted file mode 100644 index abb3efef2..000000000 Binary files a/.yarn/cache/xml-name-validator-npm-4.0.0-0857c21729-af100b79c2.zip and /dev/null differ diff --git a/.yarn/cache/xmlchars-npm-2.2.0-8b78f0f5e4-8c70ac9407.zip b/.yarn/cache/xmlchars-npm-2.2.0-8b78f0f5e4-8c70ac9407.zip deleted file mode 100644 index ed29738b8..000000000 Binary files a/.yarn/cache/xmlchars-npm-2.2.0-8b78f0f5e4-8c70ac9407.zip and /dev/null differ diff --git a/.yarn/cache/y18n-npm-5.0.8-5f3a0a7e62-54f0fb9562.zip b/.yarn/cache/y18n-npm-5.0.8-5f3a0a7e62-54f0fb9562.zip deleted file mode 100644 index bf39a466c..000000000 Binary files a/.yarn/cache/y18n-npm-5.0.8-5f3a0a7e62-54f0fb9562.zip and /dev/null differ diff --git a/.yarn/cache/yargs-npm-17.7.2-80b62638e1-73b572e863.zip b/.yarn/cache/yargs-npm-17.7.2-80b62638e1-73b572e863.zip deleted file mode 100644 index 54c49dc9c..000000000 Binary files a/.yarn/cache/yargs-npm-17.7.2-80b62638e1-73b572e863.zip and /dev/null differ diff --git a/.yarn/cache/yauzl-npm-3.2.0-22e6762265-d16440447b.zip b/.yarn/cache/yauzl-npm-3.2.0-22e6762265-d16440447b.zip deleted file mode 100644 index 0ff70a760..000000000 Binary files a/.yarn/cache/yauzl-npm-3.2.0-22e6762265-d16440447b.zip and /dev/null differ diff --git a/.yarn/cache/yn-npm-3.1.1-8ad4259784-2c487b0e14.zip b/.yarn/cache/yn-npm-3.1.1-8ad4259784-2c487b0e14.zip deleted file mode 100644 index 4a3116218..000000000 Binary files a/.yarn/cache/yn-npm-3.1.1-8ad4259784-2c487b0e14.zip and /dev/null differ diff --git a/.yarn/versions/1192494b.yml b/.yarn/versions/1192494b.yml deleted file mode 100644 index b3a28316d..000000000 --- a/.yarn/versions/1192494b.yml +++ /dev/null @@ -1,13 +0,0 @@ -undecided: - - "@courselit/docs" - - "@courselit/queue" - - "@courselit/web" - - "@courselit/common-logic" - - "@courselit/common-models" - - "@courselit/common-widgets" - - "@courselit/components-library" - - "@courselit/icons" - - "@courselit/state-management" - - tailwind-config - - "@courselit/text-editor" - - tsconfig diff --git a/.yarn/versions/443d8419.yml b/.yarn/versions/443d8419.yml deleted file mode 100644 index b3a28316d..000000000 --- a/.yarn/versions/443d8419.yml +++ /dev/null @@ -1,13 +0,0 @@ -undecided: - - "@courselit/docs" - - "@courselit/queue" - - "@courselit/web" - - "@courselit/common-logic" - - "@courselit/common-models" - - "@courselit/common-widgets" - - "@courselit/components-library" - - "@courselit/icons" - - "@courselit/state-management" - - tailwind-config - - "@courselit/text-editor" - - tsconfig diff --git a/.yarn/versions/6070ae77.yml b/.yarn/versions/6070ae77.yml deleted file mode 100644 index b3a28316d..000000000 --- a/.yarn/versions/6070ae77.yml +++ /dev/null @@ -1,13 +0,0 @@ -undecided: - - "@courselit/docs" - - "@courselit/queue" - - "@courselit/web" - - "@courselit/common-logic" - - "@courselit/common-models" - - "@courselit/common-widgets" - - "@courselit/components-library" - - "@courselit/icons" - - "@courselit/state-management" - - tailwind-config - - "@courselit/text-editor" - - tsconfig diff --git a/.yarn/versions/6db3d4bb.yml b/.yarn/versions/6db3d4bb.yml deleted file mode 100644 index b3a28316d..000000000 --- a/.yarn/versions/6db3d4bb.yml +++ /dev/null @@ -1,13 +0,0 @@ -undecided: - - "@courselit/docs" - - "@courselit/queue" - - "@courselit/web" - - "@courselit/common-logic" - - "@courselit/common-models" - - "@courselit/common-widgets" - - "@courselit/components-library" - - "@courselit/icons" - - "@courselit/state-management" - - tailwind-config - - "@courselit/text-editor" - - tsconfig diff --git a/.yarn/versions/92cc60f5.yml b/.yarn/versions/92cc60f5.yml deleted file mode 100644 index b3a28316d..000000000 --- a/.yarn/versions/92cc60f5.yml +++ /dev/null @@ -1,13 +0,0 @@ -undecided: - - "@courselit/docs" - - "@courselit/queue" - - "@courselit/web" - - "@courselit/common-logic" - - "@courselit/common-models" - - "@courselit/common-widgets" - - "@courselit/components-library" - - "@courselit/icons" - - "@courselit/state-management" - - tailwind-config - - "@courselit/text-editor" - - tsconfig diff --git a/.yarn/versions/bab16a1c.yml b/.yarn/versions/bab16a1c.yml deleted file mode 100644 index b3a28316d..000000000 --- a/.yarn/versions/bab16a1c.yml +++ /dev/null @@ -1,13 +0,0 @@ -undecided: - - "@courselit/docs" - - "@courselit/queue" - - "@courselit/web" - - "@courselit/common-logic" - - "@courselit/common-models" - - "@courselit/common-widgets" - - "@courselit/components-library" - - "@courselit/icons" - - "@courselit/state-management" - - tailwind-config - - "@courselit/text-editor" - - tsconfig diff --git a/.yarn/versions/ca283e23.yml b/.yarn/versions/ca283e23.yml deleted file mode 100644 index b3a28316d..000000000 --- a/.yarn/versions/ca283e23.yml +++ /dev/null @@ -1,13 +0,0 @@ -undecided: - - "@courselit/docs" - - "@courselit/queue" - - "@courselit/web" - - "@courselit/common-logic" - - "@courselit/common-models" - - "@courselit/common-widgets" - - "@courselit/components-library" - - "@courselit/icons" - - "@courselit/state-management" - - tailwind-config - - "@courselit/text-editor" - - tsconfig diff --git a/apps/docs/public/assets/pages/page-builder-blocks.png b/apps/docs/public/assets/pages/page-builder-blocks.png new file mode 100644 index 000000000..81f7884bb Binary files /dev/null and b/apps/docs/public/assets/pages/page-builder-blocks.png differ diff --git a/apps/docs/public/assets/pages/page-builder-home.png b/apps/docs/public/assets/pages/page-builder-home.png new file mode 100644 index 000000000..82a195429 Binary files /dev/null and b/apps/docs/public/assets/pages/page-builder-home.png differ diff --git a/apps/docs/public/assets/pages/themes/classic-theme.png b/apps/docs/public/assets/pages/themes/classic-theme.png new file mode 100644 index 000000000..36832ff47 Binary files /dev/null and b/apps/docs/public/assets/pages/themes/classic-theme.png differ diff --git a/apps/docs/public/assets/pages/themes/edit-theme-button.png b/apps/docs/public/assets/pages/themes/edit-theme-button.png new file mode 100644 index 000000000..bb35b9afc Binary files /dev/null and b/apps/docs/public/assets/pages/themes/edit-theme-button.png differ diff --git a/apps/docs/public/assets/pages/themes/editorial-theme.png b/apps/docs/public/assets/pages/themes/editorial-theme.png new file mode 100644 index 000000000..59e8c558c Binary files /dev/null and b/apps/docs/public/assets/pages/themes/editorial-theme.png differ diff --git a/apps/docs/public/assets/pages/themes/learning-theme.png b/apps/docs/public/assets/pages/themes/learning-theme.png new file mode 100644 index 000000000..b40c41ff6 Binary files /dev/null and b/apps/docs/public/assets/pages/themes/learning-theme.png differ diff --git a/apps/docs/public/assets/pages/themes/midnight-theme.png b/apps/docs/public/assets/pages/themes/midnight-theme.png new file mode 100644 index 000000000..432c402c5 Binary files /dev/null and b/apps/docs/public/assets/pages/themes/midnight-theme.png differ diff --git a/apps/docs/public/assets/pages/themes/neo-brutalism-theme.png b/apps/docs/public/assets/pages/themes/neo-brutalism-theme.png new file mode 100644 index 000000000..3378cf9fe Binary files /dev/null and b/apps/docs/public/assets/pages/themes/neo-brutalism-theme.png differ diff --git a/apps/docs/public/assets/pages/themes/publish-theme-button.png b/apps/docs/public/assets/pages/themes/publish-theme-button.png new file mode 100644 index 000000000..17e224445 Binary files /dev/null and b/apps/docs/public/assets/pages/themes/publish-theme-button.png differ diff --git a/apps/docs/public/assets/pages/themes/switch-theme-button.png b/apps/docs/public/assets/pages/themes/switch-theme-button.png new file mode 100644 index 000000000..6716a5be4 Binary files /dev/null and b/apps/docs/public/assets/pages/themes/switch-theme-button.png differ diff --git a/apps/docs/public/assets/pages/themes/theme-builder-colors-category.png b/apps/docs/public/assets/pages/themes/theme-builder-colors-category.png new file mode 100644 index 000000000..2872400bf Binary files /dev/null and b/apps/docs/public/assets/pages/themes/theme-builder-colors-category.png differ diff --git a/apps/docs/public/assets/pages/themes/theme-builder-home.png b/apps/docs/public/assets/pages/themes/theme-builder-home.png new file mode 100644 index 000000000..94dadc884 Binary files /dev/null and b/apps/docs/public/assets/pages/themes/theme-builder-home.png differ diff --git a/apps/docs/public/assets/pages/themes/theme-builder-interactives-category.png b/apps/docs/public/assets/pages/themes/theme-builder-interactives-category.png new file mode 100644 index 000000000..0cd98736a Binary files /dev/null and b/apps/docs/public/assets/pages/themes/theme-builder-interactives-category.png differ diff --git a/apps/docs/public/assets/pages/themes/theme-builder-structure-category.png b/apps/docs/public/assets/pages/themes/theme-builder-structure-category.png new file mode 100644 index 000000000..a228011c4 Binary files /dev/null and b/apps/docs/public/assets/pages/themes/theme-builder-structure-category.png differ diff --git a/apps/docs/public/assets/pages/themes/theme-builder-typography-category.png b/apps/docs/public/assets/pages/themes/theme-builder-typography-category.png new file mode 100644 index 000000000..a7285199e Binary files /dev/null and b/apps/docs/public/assets/pages/themes/theme-builder-typography-category.png differ diff --git a/apps/docs/src/config.ts b/apps/docs/src/config.ts index 34ab0220f..058bf4834 100644 --- a/apps/docs/src/config.ts +++ b/apps/docs/src/config.ts @@ -94,19 +94,11 @@ export const SIDEBAR: Sidebar = { link: "en/email-marketing/sequences", }, ], - Pages: [ - { text: "Introduction", link: "en/pages/introduction" }, - { text: "Page Blocks", link: "en/pages/blocks" }, - { text: "Edit page", link: "en/pages/edit" }, - { text: "Header", link: "en/pages/header" }, - { text: "Rich text", link: "en/pages/rich-text" }, - { text: "Featured", link: "en/pages/featured" }, - { text: "Banner", link: "en/pages/banner" }, - { text: "Hero", link: "en/pages/hero" }, - { text: "Grid", link: "en/pages/grid" }, - { text: "Curriculum", link: "en/pages/curriculum" }, - { text: "Newsletter signup", link: "en/pages/newsletter-signup" }, - { text: "Footer", link: "en/pages/footer" }, + Website: [ + { text: "Introduction", link: "en/website/introduction" }, + { text: "Page blocks", link: "en/website/blocks" }, + { text: "Edit page", link: "en/website/edit" }, + { text: "Themes", link: "en/website/themes" }, ], Blog: [ { text: "Introduction", link: "en/blog/introduction" }, diff --git a/apps/docs/src/pages/en/pages/banner.md b/apps/docs/src/pages/en/pages/banner.md deleted file mode 100644 index d260cc644..000000000 --- a/apps/docs/src/pages/en/pages/banner.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Banner block -description: Banner block -layout: ../../../layouts/MainLayout.astro ---- - -Banner block is the default block which shows the basic information about the page i.e. on sales page it shows the product's details like its title, description, featured image and pricing and on the homepage it shows your school's details like its name and subtitle. - -## Overriding details - -By default, the banner block shows the details from your product or school depending upon which type of page it is displayed on. - -These details however can be overridden on the block level. Following is how. - -1. Click on the banner to reveal its settings. -2. Change the relevant details from the `Basic` section. - -![Banner basic details](/assets/pages/banner-basic-details.png) - -## Creating a lead magnet - -The banner block can also be used as a lead magnet form. The pricing of your product should be set to `Free email delivery`. Following are the steps. - -### Steps - -1. Add the `Banner` block on your page (if not already). - -![Banner add](/assets/pages/add-banner.png) - -2. In the banner's settings screen, scroll to the `Call to action` (aka CTA) section. - ![Banner call to action](/assets/pages/banner-cta.png) - -3. In the CTA section, put the asset link which you want to share with your audience in the `Success message` text box. - ![Banner call to action download link](/assets/pages/banner-cta-download-link.png) - -> Make sure the link you are sharing here is open to public and can be easily accessed. - -4. Publish the page. - -Now, whenever your users enter their emails and press submit, they will get to see the text you entered in the `Success message` text box. - -## Stuck somewhere? - -We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/pages/blocks.md b/apps/docs/src/pages/en/pages/blocks.md deleted file mode 100644 index 3ad6495c4..000000000 --- a/apps/docs/src/pages/en/pages/blocks.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Page blocks -description: Page blocks -layout: ../../../layouts/MainLayout.astro ---- - -Every page in CourseLit is made up of various blocks, stacked in a top to bottom fashion. Each block serves a unique purpose and can be customized. - -The following screenshot shows [Header](/en/pages/header), [Banner](/en/pages/banner), [Content](/en/pages/content) and [Grid](/en/pages/grid) blocks in action. Different blocks are highlighted in different colors. - -![CourseLit page blocks](/assets/pages/page-blocks.png) - -## Types of page blocks - -CourseLit offers a wide range of page block so that you can build all sorts of web pages. - -1. [Header](/en/pages/header) -2. [Rich text](/en/pages/rich-text) -3. [Featured](/en/pages/featured) -4. [Banner](/en/pages/banner) -5. [Hero](/en/pages/hero) -6. [Grid](/en/pages/grid) -7. [Content](/en/pages/content) -8. [Newsletter signup](/en/pages/newsletter-signup) -9. [Footer](/en/pages/footer) - -## Shared blocks - -There are certain page blocks which are shared among all the pages. - -> If you change the styling of these blocks, it will be immediately published (i.e. without even pressing the `Publish` button) and will affect all the pages where these blocks are used. - -Following are the shared page blocks. - -1. [Header](/en/pages/header) -2. [Newsletter signup](/en/pages/newsletter-signup) -3. [Footer](/en/pages/footer) - -## Next step - -Now that you have learnt about page blocks, it is time to learn how to use those in your pages. See our [edit a page](/en/pages/edit) guide for details. - -## Stuck somewhere? - -We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/pages/curriculum.md b/apps/docs/src/pages/en/pages/curriculum.md deleted file mode 100644 index 50c0e7407..000000000 --- a/apps/docs/src/pages/en/pages/curriculum.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Curriculum block -description: Curriculum block -layout: ../../../layouts/MainLayout.astro ---- - -> This block can only be added to the products' sales pages. - -This block shows the content of your product i.e. `Sections` and `Lessons` in your product. Using this, you can show the index of what your product offers. - -Following is how it looks on a page. - -![Curriculum block](/assets/pages/content-block.jpeg) - -There are two sections with two lessons each in the product demonstrated above. - -Your audience can directly click on the lessons to see it on the course viewer. The preview lessons are indicated distinctly so that your audience can easily checkout the free parts of your product. - -![Curriculum block preview](/assets/pages/content-block-preview.gif) - -## Stuck somewhere? - -We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/pages/featured.md b/apps/docs/src/pages/en/pages/featured.md deleted file mode 100644 index b1aea6ac9..000000000 --- a/apps/docs/src/pages/en/pages/featured.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Featured block -description: Featured block -layout: ../../../layouts/MainLayout.astro ---- - -If you want to show your other products on a page, the featured widget is the one to use. - -Following is how it looks on a page. - -![Featured block](/assets/pages/featured-block.png) - -## Add featured products on your page - -1. Add the `Featured` block on your page. See here for how to [add blocks](/en/pages/edit#add-a-block) to a page. -2. Go to the products section and select the products from the drop down list as shown below. - -![Featured block](/assets/pages/featured.gif) - -3. For deleting an entry from the featured list, click on the delete button against the entry in the products section (also demonstrated in the above screengrab). - -## Stuck somewhere? - -We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/pages/footer.md b/apps/docs/src/pages/en/pages/footer.md deleted file mode 100644 index 3501584f9..000000000 --- a/apps/docs/src/pages/en/pages/footer.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Footer block -description: Footer block -layout: ../../../layouts/MainLayout.astro ---- - -The footer block serves as the header of a page. It is used for housing secondary but essential stuff like links to terms and conditions, privacy policies etc.. This block **cannot be deleted** from a page. - -> WARNING: This is a [shared block](/en/pages/blocks#shared-page-blocks) so any changes to this block are instantaneously published and will be visible on **all pages** of your school. - -## Stuck somewhere? - -We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/pages/grid.md b/apps/docs/src/pages/en/pages/grid.md deleted file mode 100644 index df3cfd18d..000000000 --- a/apps/docs/src/pages/en/pages/grid.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Grid block -description: Grid block -layout: ../../../layouts/MainLayout.astro ---- - -A grid block comes handy when you want to show some sort of list. For example, features list or advantages etc. The list gets displayed in the grid format as shown below. - -![Grid block](/assets/pages/grid-block.png) - -## Add an item - -1. Click on the grid block to reveal its settings. -2. Scroll down to the `Items` section as shown below. - -![Grid block items](/assets/pages/grid-block-items.png) - -3. Click on the `Add new item` button as shown above. This will open up the item's editor. -4. Change the details as per your likings. See [customizing the call to action button](#customizing-the-call-to-action-button) guide to check how to customise item's call to action button. Once done, click on the `Done` button. - -![Grid block edit item](/assets/pages/grid-add-item.png) - -## Delete an item - -1. Click on the grid block to reveal its settings. -2. Scroll down to the `Items` section as shown below. - -![Grid block items](/assets/pages/grid-block-items.png) - -3. Click on the item you want to remove. This will open up the item's editor. -4. Click on the delete button to delete the item. -5. You will be taken back to the grid block's settings. - -## Customizing the call to action button - -1. Click on the grid block to reveal its settings. -2. Scroll to the `Call to action` section. - -![Grid block CTA](/assets/pages/grid-block-cta.png) - -3. In the button text field, add the text that will be visible on the button. -4. In the button action, enter the URL the user should be taken on upon click. - -a). If the URL is from the own school, use its relative form i.e. `/courses`. - -b). If the URL if from some external website, use the absolute (complete) URL i.e. `https://website.com/courses`. - -## Stuck somewhere? - -We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/pages/header.md b/apps/docs/src/pages/en/pages/header.md deleted file mode 100644 index a8e06505d..000000000 --- a/apps/docs/src/pages/en/pages/header.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Header block -description: Header block -layout: ../../../layouts/MainLayout.astro ---- - -The header block serves as the header of a page. It is used for housing site's navigation etc. This block cannot be deleted from a page. - -> WARNING: This is a [shared block](/en/pages/blocks#shared-page-blocks) so any changes to this block are instantaneously published and will be visible on **all pages** of your school. - -## Adding links - -You can add navigation links by following the steps below. - -1. Click on the header block to reveal its settings side pane. -2. Click on the `Add new link` button in the `Links` section. This will add a new `Link` item as shown below. - ![Header add link](/assets/pages/header-link-add.png) - -You will also see the newly added link on the header itself. - -3. Click on the pencil icon against the newly added link to edit it as shown above. -4. Change the label (displayed at text on the header block) and the URL (where the user should be taken to upon clicking the label on the header) and click `Done` to save. - ![Header edit link](/assets/pages/header-edit-link.png) - -## Design - -The design section gives you access to all the settings to customize the header's look. - -## Stuck somewhere? - -We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/pages/hero.md b/apps/docs/src/pages/en/pages/hero.md deleted file mode 100644 index 77f3cb828..000000000 --- a/apps/docs/src/pages/en/pages/hero.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Hero block -description: Hero block -layout: ../../../layouts/MainLayout.astro ---- - -A hero section of a web page is the section which immediately shows up on screen, just under the header. The hero block helps you in putting the information front and center. - -You can add text, rich text, images and a call to action (CTA) button to the hero block. - -Following is how it looks on a page. - -![Hero block](/assets/pages/hero-block.png) - -## Customizing the call to action button - -1. Click on the hero block to reveal its settings. -2. Scroll to the `Call to action` section. - -![Hero block CTA](/assets/pages/hero-block-cta.png) - -3. In the button text field, add the text that will be visible on the button. -4. In the button action, enter the URL the user should be taken on upon click. - -a). If the URL is from the own school, use its relative form i.e. `/courses`. - -b). If the URL if from some external website, use the absolute (complete) URL i.e. `https://website.com/courses`. - -## Stuck somewhere? - -We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/pages/newsletter-signup.md b/apps/docs/src/pages/en/pages/newsletter-signup.md deleted file mode 100644 index a3e52319c..000000000 --- a/apps/docs/src/pages/en/pages/newsletter-signup.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: Newsletter signup block -description: Newsletter signup block -layout: ../../../layouts/MainLayout.astro ---- - -Having a mailing list to sell directly to is a dream of every business, big or small. That's why CourseLit offers a dedicated block that lets you capture emails. It is also a [shared block](/en/pages/blocks#shared-page-blocks). - -Following is how it looks on a page. - -![Newsletter signup block](/assets/pages/newsletter-signup-block.png) - -> WARNING: This is a [shared block](/en/pages/blocks#shared-page-blocks) so any changes to this block are instantaneously published and will be visible on **all pages** of your school. - -## How it works - -1. Your audience will enter their emails in the text box on the Newsletter signup block. -2. A user is created in your school. -3. The user is automatically signed up for your newsletter. -4. You can see the user in the `Users` section from the dashboard. - -Following is an animation that shows the entire flow. - -![Newsletter signup block working](/assets/pages/newsletter-signup-block-working.gif) - -## Stuck somewhere? - -We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/website/blocks.md b/apps/docs/src/pages/en/website/blocks.md new file mode 100644 index 000000000..a937bd2c6 --- /dev/null +++ b/apps/docs/src/pages/en/website/blocks.md @@ -0,0 +1,285 @@ +--- +title: Page blocks +description: Page blocks +layout: ../../../layouts/MainLayout.astro +--- + +Every page in CourseLit is made up of various blocks, stacked in a top-to-bottom fashion. Each block serves a unique purpose and can be customized. + +The following screenshot shows [Header](/en/pages/header), [Rich Text](/en/pages/banner), [Hero](/en/pages/content), and [Grid](/en/pages/grid) blocks (top to bottom) in action. Different blocks are highlighted in different colors. + +![CourseLit page blocks](/assets/pages/page-builder-blocks.png) + +## Blocks + +CourseLit offers a wide range of page blocks so that you can build all sorts of web pages. + +### [Header](#header) + +
+Expand to see Header block details + +> This is a [shared block](#shared-blocks). All published changes to this block impact all pages on your website. + +The header block serves as the header of a page. It is used for housing the site's navigation, etc. This block cannot be deleted from a page. + +#### Adding links + +You can add navigation links by following the steps below. + +1. Click on the header block to reveal its settings side pane. +2. Click on the `Add new link` button in the `Links` section. This will add a new `Link` item as shown below. + ![Header add link](/assets/pages/header-link-add.png) + +You will also see the newly added link on the header itself. + +3. Click on the pencil icon against the newly added link to edit it as shown above. +4. Change the label (displayed as text on the header block) and the URL (where the user should be taken upon clicking the label on the header) and click `Done` to save. +![Header edit link](/assets/pages/header-edit-link.png) +
+ +### [Rich Text](#rich-text) + +
+Expand to see Rich Text block details + +The rich text block can be used to add text blocks containing elements like hyperlinks, etc. + +#### Making text bold/italic/underline + +1. Select the text. +2. To make the selected text bold, press Ctrl+B; to make it italic, press Ctrl+I; and for underline, press Ctrl+U. + +You can also use the floating controls to do the same as shown below. + +![Stylised text](/assets/pages/rich-text-styling.gif) + +#### Creating hyperlinks + +1. Select the text. + > Double-clicking the text to select won't work due to a bug. We are working on it. +2. Click on the floating `link` button to reveal a popup text input. +3. In the popup text input, enter the URL as shown below. +![Create a hyperlink in rich text block](/assets/pages/rich-text-create-hyperlink.gif) +
+ +### [Hero](#hero) + +
+Expand to see Hero block details + +A hero section of a web page is the section that immediately appears on screen, just under the header. The hero block helps you put the information front and center. + +You can add text, rich text, images, and a call-to-action (CTA) button to the hero block. + +Following is how it looks on a page. + +![Hero block](/assets/pages/hero-block.png) + +#### Customizing the call-to-action button + +1. Click on the hero block to reveal its settings. +2. Scroll to the `Call to action` section. + +![Hero block CTA](/assets/pages/hero-block-cta.png) + +3. In the button text field, add the text that will be visible on the button. +4. In the button action, enter the URL the user should be taken to upon clicking. +a. If the URL is from your own school, use its relative form, i.e., `/courses`. +b. If the URL is from some external website, use the absolute (complete) URL, i.e., `https://website.com/courses`. +
+ +### [Grid](#grid) + +
+Expand to see Grid block details + +A grid block comes in handy when you want to show some sort of list, for example, features list or advantages, etc. The list gets displayed in the grid format as shown below. + +![Grid block](/assets/pages/grid-block.png) + +#### Add an item + +1. Click on the grid block to reveal its settings. +2. Scroll down to the `Items` section as shown below. + +![Grid block items](/assets/pages/grid-block-items.png) + +3. Click on the `Add new item` button as shown above. This will open up the item's editor. +4. Change the details as per your liking. See the [customizing the call-to-action button](#customizing-the-call-to-action-button) guide to check how to customize the item's call-to-action button. Once done, click on the `Done` button. + +![Grid block edit item](/assets/pages/grid-add-item.png) + +#### Delete an item + +1. Click on the grid block to reveal its settings. +2. Scroll down to the `Items` section as shown below. + +![Grid block items](/assets/pages/grid-block-items.png) + +3. Click on the item you want to remove. This will open up the item's editor. +4. Click on the delete button to delete the item. +5. You will be taken back to the grid block's settings. + +#### Customizing the call-to-action button + +1. Click on the grid block to reveal its settings. +2. Scroll to the `Call to action` section. + +![Grid block CTA](/assets/pages/grid-block-cta.png) + +3. In the button text field, add the text that will be visible on the button. +4. In the button action, enter the URL the user should be taken to upon clicking. +a. If the URL is from your own school, use its relative form, i.e., `/courses`. +b. If the URL is from some external website, use the absolute (complete) URL, i.e., `https://website.com/courses`. +
+ +### [Featured](#featured) + +
+Expand to see Featured block details + +If you want to show your other products on a page, the featured widget is the one to use. + +Following is how it looks on a page. + +![Featured block](/assets/pages/featured-block.png) + +#### Add featured products on your page + +1. Add the `Featured` block on your page. See here for how to [add blocks](/en/pages/edit#add-a-block) to a page. +2. Go to the products section and select the products from the dropdown list as shown below. + +![Featured block](/assets/pages/featured.gif) + +3. To delete an entry from the featured list, click on the delete button against the entry in the products section (also demonstrated in the above screengrab). +
+ +### [Curriculum](#curriculum) + +
+Expand to see Curriculum block details + +> This block can only be added to the products' sales pages. + +This block shows the content of your product, i.e., `Sections` and `Lessons` in your product. Using this, you can show the index of what your product offers. + +Following is how it looks on a page. + +![Curriculum block](/assets/pages/content-block.jpeg) + +There are two sections with two lessons each in the product demonstrated above. + +Your audience can directly click on the lessons to see them in the course viewer. The preview lessons are indicated distinctly so that your audience can easily check out the free parts of your product. + +![Curriculum block preview](/assets/pages/content-block-preview.gif) + +
+ +### [Banner](#banner) + +
+Expand to see Banner block details + +The banner block is the default block that shows the basic information about the page, i.e., on a sales page it shows the product's details like its title, description, featured image, and pricing, and on the homepage it shows your school's details like its name and subtitle. + +#### Overriding details + +By default, the banner block shows the details from your product or school depending upon which type of page it is displayed on. + +These details, however, can be overridden at the block level. Following is how: + +1. Click on the banner to reveal its settings. +2. Change the relevant details from the `Basic` section. + +![Banner basic details](/assets/pages/banner-basic-details.png) + +#### Creating a lead magnet + +The banner block can also be used as a lead magnet form. The pricing of your product should be set to `Free email delivery`. Following are the steps: + +##### Steps + +1. Add the `Banner` block on your page (if not already present). + +![Banner add](/assets/pages/add-banner.png) + +2. In the banner's settings screen, scroll to the `Call to action` (aka CTA) section. + ![Banner call to action](/assets/pages/banner-cta.png) + +3. In the CTA section, put the asset link which you want to share with your audience in the `Success message` text box. + ![Banner call to action download link](/assets/pages/banner-cta-download-link.png) + +> Make sure the link you are sharing here is open to the public and can be easily accessed. + +4. Publish the page. + +Now, whenever your users enter their emails and press submit, they will see the text you entered in the `Success message` text box. + +
+ +### [Newsletter signup](#newsletter-signup) + +
+Expand to see Newsletter signup block details + +Having a mailing list to sell directly to is a dream of every business, big or small. That's why CourseLit offers a dedicated block that lets you capture emails. It is also a [shared block](/en/pages/blocks#shared-page-blocks). + +Following is how it looks on a page. + +![Newsletter signup block](/assets/pages/newsletter-signup-block.png) + +#### How it works + +1. Your audience will enter their emails in the text box on the Newsletter signup block. +2. A user is created in your school. +3. The user is automatically signed up for your newsletter. +4. You can see the user in the `Users` section from the dashboard. + +Following is an animation that shows the entire flow. + +![Newsletter signup block working](/assets/pages/newsletter-signup-block-working.gif) + +
+ +### [Footer](#footer) + +
+Expand to see Footer block details + +> This is a [shared block](#shared-blocks). All published changes to this block impact all pages on your website. + +The footer block serves as the footer of a page. It is used for housing secondary but essential elements like links to terms and conditions, privacy policies, etc. This block **cannot be deleted** from a page. + +#### Adding sections and links + +1. Click on the footer block to reveal its settings. +2. In the `Sections` panel, you can: + - Add new sections (up to 5 sections) + - Rename sections + - Add, edit, or delete links within each section + - Reorder links using drag and drop + +#### Customizing design + +In the `Design` panel, you can customize: + +- Title font size +- Maximum width +- Vertical padding +- Social media links (Facebook, Twitter, Instagram, LinkedIn, YouTube, Discord, GitHub) +
+ +## [Shared blocks](#shared-blocks) + +The `Header` and `Footer` are shared among all the website pages. These cannot be deleted from a page. + +Since these are shared, publishing any changes to these will make those visible across your website. + +## Next step + +Now that you have learned about page blocks, it is time to learn how to use them in your pages. See our [edit a page](/en/pages/edit) guide for details. + +## Stuck somewhere? + +We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/pages/edit.md b/apps/docs/src/pages/en/website/edit.md similarity index 100% rename from apps/docs/src/pages/en/pages/edit.md rename to apps/docs/src/pages/en/website/edit.md diff --git a/apps/docs/src/pages/en/pages/introduction.md b/apps/docs/src/pages/en/website/introduction.md similarity index 71% rename from apps/docs/src/pages/en/pages/introduction.md rename to apps/docs/src/pages/en/website/introduction.md index 4fc9dcc63..346b3c3e9 100644 --- a/apps/docs/src/pages/en/pages/introduction.md +++ b/apps/docs/src/pages/en/website/introduction.md @@ -1,18 +1,18 @@ --- -title: Pages -description: Pages +title: Website +description: Introduction to CourseLit's Build-in Website Builder layout: ../../../layouts/MainLayout.astro --- -CourseLit comes with a powerful page builder. Using the page builder, you can build all sorts of web pages for various use cases like landing pages, sales pages, email list builder etc. +CourseLit comes with a powerful website builder. Using the website builder, you can build all sorts of web pages for various use cases like landing pages, sales pages, email list builder etc. -![CourseLit's Page builder](/assets/pages/page-builder-sections.png) +![CourseLit's Page builder](/assets/pages/page-builder-home.png) ## Default pages When you create a new school, the following pages are automatically created for you. -1. Homepage +1. Home page This is your school's landing page hosted at `/`. @@ -34,7 +34,7 @@ Each product that you create, gets its own sales page. This comes handy if you w ## Next step -[Learn about page blocks](/en/pages/blocks). +[Learn about page blocks](/en/website/blocks). ## Stuck somewhere? diff --git a/apps/docs/src/pages/en/pages/rich-text.md b/apps/docs/src/pages/en/website/rich-text.md similarity index 100% rename from apps/docs/src/pages/en/pages/rich-text.md rename to apps/docs/src/pages/en/website/rich-text.md diff --git a/apps/docs/src/pages/en/website/themes.md b/apps/docs/src/pages/en/website/themes.md new file mode 100644 index 000000000..0700d2e29 --- /dev/null +++ b/apps/docs/src/pages/en/website/themes.md @@ -0,0 +1,542 @@ +--- +title: Themes +description: Introduction to CourseLit's Theme Designer +layout: ../../../layouts/MainLayout.astro +--- + +CourseLit's Theme Designer is a powerful tool that allows you to customize the look and feel of your learning platform. + +Built on top of [shadcn's design system](https://ui.shadcn.com/), it provides a comprehensive set of options to create a unique and professional appearance for your website. + +Every theme in CourseLit comes in two flavors - light and dark mode. This ensures your website looks great regardless of the user's system preferences or time of day. + +![CourseLit Theme Builder](/assets/pages/themes/theme-builder-home.png) + +Currently, themes are only applied to the public pages of your school. The dashboard uses the system theme to provide a consistent experience. If you'd like themes to be applied to the dashboard as well, please leave your upvote here. The system theme also includes a dark mode. + +## Table of Contents + +- [System Themes](#system-themes) +- [Custom Themes](#custom-themes) +- [Previewing a Theme](#previewing-a-theme) +- [Customizing a Theme](#customizing-a-theme) + - [Colors](#1-colors) + - [Typography](#2-typography) + - [Interactives](#3-interactives) + - [Structure](#4-structure) +- [Publishing Changes](#5-publishing-changes) +- [Switching Themes](#switching-themes) +- [Supported Tailwind Classes](#supported-tailwind-classes) +- [Need Help?](#need-help) + +## System Themes + +System themes are pre-built themes that come with CourseLit. These themes are professionally designed and ready to use. They serve as a great starting point for your website's appearance. + +Currently, CourseLit offers the following system themes: + +
+Classic (Default) + +A clean, professional theme with balanced typography and subtle colors +![Classic Theme Preview](/assets/pages/themes/classic-theme.png) + +
+ +
+Learning + +Optimized for educational content with enhanced readability +![Learning Theme Preview](/assets/pages/themes/learning-theme.png) + +
+ +
+Neobrutalism + +A bold, modern theme with strong typography and vibrant colors +![Neobrutalism Theme Preview](/assets/pages/themes/neo-brutalism-theme.png) + +
+ +
+Editorial + +A sophisticated theme inspired by editorial design +![Editorial Theme Preview](/assets/pages/themes/editorial-theme.png) + +
+ +
+Midnight + +A dark-focused theme with high contrast and dramatic elements +![Midnight Theme Preview](/assets/pages/themes/midnight-theme.png) + +
+ +Each system theme can be used as-is or customized to match your brand identity. + +## Custom Themes + +Custom themes are your own creations or modifications of system themes. When you customize a system theme, CourseLit automatically creates a copy of it, allowing you to make changes without affecting the original. + +To create a custom theme: + +1. Select a system theme as your starting point +2. Click the "Edit" button to open the theme editor +3. Make your desired changes +4. To edit the dark mode settings, switch the theme using the moon icon (🌙) and make the changes + +Your custom themes will appear in the "Custom Themes" section of the theme selector. + +> When you edit a theme, CourseLit remembers it and automatically selects it the next time you open the theme editor. + +## Previewing a Theme + +Click on the theme card to preview it on your school. This will not switch the theme. + +## Customizing a Theme + +The theme editor gives you complete control over your website's look and feel. To start editing a theme, click on the pencil icon button on the theme's card. + +![Edit theme button](/assets/pages/themes/edit-theme-button.png) + +#### Understanding the auto save feature + +Your theme changes are automatically saved as you work, so you can take your time perfecting the design. + +These changes are stored as a draft version of your theme and are only visible while you're actively editing that theme in the theme editor. When you're ready to apply your changes to your live website, simply set the theme as active and click the publish button. + +The theme editor offers several categories of customization options: + +### 1. Colors + +![Color Editor](/assets/pages/themes/theme-builder-colors-category.png) + +The color editor allows you to customize various aspects of your theme's color scheme: + +
+Primary Colors +Your main brand colors used for important actions and key elements +
+ +
+Secondary Colors +Supporting colors that complement your primary colors +
+ +
+Accent Colors +Highlight colors for special elements and calls-to-action +
+ +
+Base Colors +Fundamental colors for your website's background and text +
+ +
+Card Colors +Colors for card components and content boxes +
+ +
+Popover Colors +Colors for floating menus and tooltips +
+ +
+Muted Colors +Subtle colors for less prominent elements +
+ +
+Border & Input Colors +Colors for borders and form fields +
+ +
+Destructive Colors +Colors for error states and warning messages +
+ +
+Chart Colors +Colors for data visualization and graphs +
+ +
+Sidebar Colors +Colors for the navigation sidebar +
+ +
+Shadow Styles +Customize shadow effects for depth and elevation +
+ +> **For Developers**: These settings correspond 1-to-1 to ShadCN's [CSS variables](https://ui.shadcn.com/docs/theming). + +### 2. Typography + +![Typography Editor](/assets/pages/themes/theme-builder-typography-category.png) + +The typography editor lets you customize text styles across your website. These are organized into categories: + +
+Headers + +- Header 1: Large titles for main page headings +- Header 2: Medium titles for major sections +- Header 3: Smaller titles for subsections +- Header 4: Small titles for minor sections +- Preheader: Introductory text that appears above headers +
+ +
+Subheaders + +- Subheader 1: Primary subheaders for section introductions +- Subheader 2: Secondary subheaders for supporting text +
+ +
+Body Text + +- Text 1: Main body text for content +- Text 2: Secondary body text for supporting content +- Caption: Small text for image captions and footnotes +
+ +
+Interactive Elements + +- Link: Text for clickable links +- Button: Text for buttons and calls-to-action +- Input: Text for form fields and search boxes +
+ +For each text style, you can customize: + +- Font family: Choose from a variety of professional fonts +- Font size: From Extra Small to 9X Large +- Font weight: From Thin to Black +- Line height: From None to Loose +- Letter spacing: From Tighter to Widest +- Text transform: None, Uppercase, Lowercase, or Capitalize +- Text decoration: Underline or no underline +- Text overflow: How text behaves when it's too long + +#### Supported Fonts + +CourseLit provides a carefully curated selection of professional fonts, organized into categories: + +
+Sans Serif Fonts + +- **Inter**: A modern, clean font perfect for digital interfaces +- **Open Sans**: A highly readable font designed for screen use +- **Source Sans 3**: A versatile font with excellent legibility +- **Noto Sans**: A font designed to support all languages +- **Roboto**: Google's signature font, clean and modern +- **Mulish**: A geometric sans-serif with a modern feel +- **Nunito**: A well-balanced font with rounded terminals +- **Work Sans**: A clean, modern font with a geometric feel +
+ +
+Serif Fonts + +- **Merriweather**: A serif font designed for comfortable reading +- **Alegreya**: A serif font with a calligraphic feel +- **Playfair Display**: An elegant serif font for headings +- **Roboto Slab**: A serif variant of Roboto +- **Source Serif 4**: A serif font designed for digital reading +
+ +
+Display Fonts + +- **Montserrat**: A geometric sans-serif with a modern feel +- **Poppins**: A geometric sans-serif with a unique character +- **Raleway**: An elegant sans-serif with a unique 'w' +- **Rubik**: A sans-serif with a geometric feel +- **Oswald**: A reworking of the classic style +- **Bebas Neue**: A display font with a strong personality +
+ +
+Modern Fonts + +- **Lato**: A sans-serif font with a warm feel +- **PT Sans**: A font designed for public use +- **Quicksand**: A display sans-serif with rounded terminals +
+ +Each font is optimized for web use and includes multiple weights for flexibility in design. All fonts support Latin characters and are carefully selected for their readability and professional appearance. + +### 3. Interactives + +![Interactives Editor](/assets/pages/themes/theme-builder-interactives-category.png) + +The interactives editor allows you to customize the appearance of interactive elements: + +
+Button + +- Padding: Space around the button text (None to 9X Large) +- Border style: Choose from None, Solid, Dashed, Dotted, Double, or Hidden +- Shadow effects: From None to 2X Large +- Custom styles: Add your own custom styles using [supported Tailwind classes](#supported-tailwind-classes) +- Disabled state: How the button looks when it can't be clicked +
+ +
+Link + +- Padding: Space around the link text +- Border style: Choose from various border styles +- Text shadow: Add depth to your links +- Custom styles: Add your own custom styles using [supported Tailwind classes](#supported-tailwind-classes) +- Disabled state: How the link looks when it can't be clicked +
+ +
+Card + +- Padding: Space around the card content +- Border style: Choose from various border styles +- Shadow effects: Add depth to your cards +- Custom styles: Add your own custom styles using [supported Tailwind classes](#supported-tailwind-classes) +
+ +
+Input + +- Border radius: Round the corners (None to Full) +- Padding: Space inside the input field +- Border style: Choose from various border styles +- Shadow effects: Add depth to your input fields +- Custom styles: Add your own custom styles using [supported Tailwind classes](#supported-tailwind-classes) +- Disabled state: How the input looks when it can't be used +
+ +### 4. Structure + +![Structure Editor](/assets/pages/themes/theme-builder-structure-category.png) + +The structure editor lets you customize the layout of your pages, like section paddings and maximum page width. + +
+Page + +- Maximum width options: - 2XL (42rem): Compact layout - 3XL (48rem): Standard layout - 4XL (56rem): Wide layout - 5XL (64rem): Extra wide layout - 6XL (72rem): Full width layout +
+ +
+Section + +- Horizontal padding: Space on the left and right sides (None to 9X Large) +- Vertical padding: Space on the top and bottom (None to 9X Large) +
+ +## Publishing Changes + +All changes you make to a theme are saved as drafts. To make your changes live: + +1. Review your changes in the theme editor +2. Set the theme as active on your school +3. Click the "Publish" button to publish both content and theme changes +4. Confirm the publication + +![Publish theme button](/assets/pages/themes/publish-theme-button.png) + +Your changes will now be visible to your website visitors. + +## Switching Themes + +To switch between themes: + +1. Go to the theme selector +2. Choose the theme you like +3. Click the button with a tick icon on your desired theme +4. The theme will be applied immediately + +![Switch theme button](/assets/pages/themes/switch-theme-button.png) + +Note: Switching themes will affect your entire website's appearance. Make sure to test the theme thoroughly before making it live. + +## Supported Tailwind Classes + +When adding custom styles to interactive elements, you can use the following Tailwind classes: + +
+Typography + +#### Font Sizes + +- `text-sm`: Small text +- `text-base`: Base text size +- `text-lg`: Large text +- `text-xl`: Extra large text +- `text-2xl`: 2X large text +- `text-3xl`: 3X large text +- `text-4xl`: 4X large text +- `text-5xl`: 5X large text +- `text-6xl`: 6X large text +- `text-7xl`: 7X large text +- `text-8xl`: 8X large text +
+ +
+Padding + +#### Vertical Padding + +- `py-4` to `py-20`: Vertical padding from 1rem to 5rem + +#### Horizontal Padding + +- `px-4` to `px-20`: Horizontal padding from 1rem to 5rem +
+ +
+Colors + +#### Background Colors + +- `bg-{color}-{shade}`: Where color is one of: + - slate, gray, zinc, neutral, stone + - red, orange, amber, yellow, lime + - green, emerald, teal, cyan, sky + - blue, indigo, violet, purple + - fuchsia, pink, rose +- And shade is one of: + - 50, 100, 200, 300, 400 + - 500, 600, 700, 800, 900, 950 + +#### Text Colors + +- `text-{color}-{shade}`: Same color and shade options as background colors + +Variants available: `hover`, `disabled`, `dark` + +
+ +
+Transitions + +#### Transition Properties + +- `transition`: All properties +- `transition-colors`: Colors only +- `transition-opacity`: Opacity only +- `transition-shadow`: Shadow only +- `transition-transform`: Transform only +- `transition-none`: No transition + +#### Duration + +- `duration-0`: No duration +- `duration-75`: 75ms +- `duration-100`: 100ms +- `duration-150`: 150ms +- `duration-200`: 200ms +- `duration-300`: 300ms +- `duration-500`: 500ms +- `duration-700`: 700ms +- `duration-1000`: 1000ms + +#### Timing Functions + +- `ease-in`: Ease in +- `ease-out`: Ease out +- `ease-in-out`: Ease in and out +- `ease-linear`: Linear +
+ +
+Transforms + +#### Translate X + +- `translate-x-1` to `translate-x-10`: Move right +- `translate-x-[-1]` to `translate-x-[-10]`: Move left + +#### Translate Y + +- `translate-y-1` to `translate-y-10`: Move down +- `translate-y-[-1]` to `translate-y-[-10]`: Move up + +#### Scale + +- `scale-0`: No scale +- `scale-50`: 50% scale +- `scale-75`: 75% scale +- `scale-90`: 90% scale +- `scale-95`: 95% scale +- `scale-100`: 100% scale +- `scale-105`: 105% scale +- `scale-110`: 110% scale +- `scale-125`: 125% scale +- `scale-150`: 150% scale +
+ +
+Shadows + +- `shadow-sm`: Small shadow +- `shadow-md`: Medium shadow +- `shadow-lg`: Large shadow +- `shadow-xl`: Extra large shadow +- `shadow-2xl`: 2X large shadow +- `shadow-inner`: Inner shadow +- `shadow-none`: No shadow + +Variants available: `hover`, `dark` + +
+ +
+Borders + +- `border-solid`: Solid border +- `border-dashed`: Dashed border +- `border-dotted`: Dotted border +- `border-double`: Double border +- `border-none`: No border + +Variants available: `hover` + +
+ +
+Text Decoration + +- `underline`: Underline text + +Variants available: `hover` + +
+ +
+Layout + +#### Maximum Width + +- `max-w-2xl`: 42rem +- `max-w-3xl`: 48rem +- `max-w-4xl`: 56rem +- `max-w-5xl`: 64rem +- `max-w-6xl`: 72rem + +Variants available: `lg` + +
+ +> Note: All `hover` variants are available for interactive elements. `dark` mode variants are available for colors and shadows. + +## Need Help? + +We're here to help! Join our [Discord community](https://discord.com/invite/GR4bQsN) or reach out to us on [Twitter](https://twitter.com/courselit) for support. diff --git a/apps/web/.migrations/02-06-25_10-21-unset-paddings-page.js b/apps/web/.migrations/02-06-25_10-21-unset-paddings-page.js new file mode 100644 index 000000000..ad80fb18e --- /dev/null +++ b/apps/web/.migrations/02-06-25_10-21-unset-paddings-page.js @@ -0,0 +1,218 @@ +import mongoose from "mongoose"; +import { nanoid } from "nanoid"; + +function generateUniqueId() { + return nanoid(); +} + +mongoose.connect(process.env.DB_CONNECTION_STRING, { + useNewUrlParser: true, + useUnifiedTopology: true, +}); + +const WidgetSchema = new mongoose.Schema({ + widgetId: { type: String, required: true, default: generateUniqueId }, + name: { type: String, required: true }, + deleteable: { type: Boolean, required: true, default: true }, + shared: { type: Boolean, required: true, default: false }, + settings: mongoose.Schema.Types.Mixed, +}); + +const MediaSchema = new mongoose.Schema({ + mediaId: { type: String, required: true }, + originalFileName: { type: String, required: true }, + mimeType: { type: String, required: true }, + size: { type: Number, required: true }, + access: { type: String, required: true, enum: ["public", "private"] }, + thumbnail: String, + caption: String, + file: String, +}); + +const PageSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + pageId: { type: String, required: true }, + type: { + type: String, + required: true, + enum: ["product", "site", "blog", "community"], + default: "product", + }, + creatorId: { type: String, required: true }, + name: { type: String, required: true }, + layout: { type: [WidgetSchema], default: [] }, + draftLayout: { type: [WidgetSchema], default: [] }, + entityId: { type: String }, + deleteable: { type: Boolean, required: true, default: false }, + title: { type: String }, + description: String, + socialImage: MediaSchema, + robotsAllowed: { type: Boolean, default: true }, + draftTitle: String, + draftDescription: String, + draftSocialImage: MediaSchema, + draftRobotsAllowed: Boolean, + deleted: { type: Boolean, default: false }, + }, + { + timestamps: true, + }, +); + +PageSchema.index( + { + domain: 1, + pageId: 1, + }, + { unique: true }, +); + +const Page = mongoose.model("Page", PageSchema); + +const DomainSchema = new mongoose.Schema( + { + name: { type: String, required: true, unique: true }, + sharedWidgets: { + type: mongoose.Schema.Types.Mixed, + default: {}, + }, + draftSharedWidgets: { + type: mongoose.Schema.Types.Mixed, + default: {}, + }, + }, + { + timestamps: true, + }, +); +const Domain = mongoose.model("Domain", DomainSchema); + +const unsetPaddingsOnPage = async (page, domain) => { + console.log(`Migrating ${page.pageId} of ${page.domain}`); + + if (page.layout && page.layout.length > 0) { + page.layout.forEach((widget) => { + if (widget.name === "newsletter-signup") { + widget.shared = false; + widget.settings = + domain.sharedWidgets["newsletter-signup"]?.settings; + } + + if (widget.settings) { + delete widget.settings.horizontalPadding; + delete widget.settings.verticalPadding; + } + }); + } + + if (page.draftLayout && page.draftLayout.length > 0) { + page.draftLayout.forEach((widget) => { + if (widget.name === "newsletter-signup") { + widget.shared = false; + widget.settings = + domain.draftSharedWidgets["newsletter-signup"]?.settings; + } + + if (widget.settings) { + delete widget.settings.horizontalPadding; + delete widget.settings.verticalPadding; + } + }); + } + + page.markModified("layout"); + page.markModified("draftLayout"); + await page.save(); + console.log(`Migrated ${page.pageId} of ${page.domain}\n`); +}; + +const migratePages = async () => { + const pages = await Page.find({}); + for (const page of pages) { + try { + const domain = await Domain.findOne({ _id: page.domain }); + await unsetPaddingsOnPage(page, domain); + } catch (error) { + console.error(`Error migrating ${page.pageId} of ${page.domain}`); + console.error(error); + } + } +}; + +const unsetPaddingOnSharedWidgets = async (domain) => { + console.log(`Migrating shared widgets for domain: ${domain.name}`); + if (Object.keys(domain.sharedWidgets).length > 0) { + for (const widget of Object.values(domain.sharedWidgets)) { + if (widget.settings) { + delete widget.settings.horizontalPadding; + delete widget.settings.verticalPadding; + } + } + } + + if ( + domain.draftSharedWidgets && + Object.keys(domain.draftSharedWidgets).length > 0 + ) { + for (const widget of Object.values(domain.draftSharedWidgets)) { + if (widget.settings) { + delete widget.settings.horizontalPadding; + delete widget.settings.verticalPadding; + } + } + } + + domain.markModified("sharedWidgets"); + domain.markModified("draftSharedWidgets"); + await domain.save(); + console.log(`Migrated shared widgets for domain: ${domain.name}\n`); +}; + +const removeNewsLetterSignupFromSharedWidgets = async (domain) => { + console.log( + `Migrating news letter signup from shared widgets for domain: ${domain.name}`, + ); + if (domain.sharedWidgets && Object.keys(domain.sharedWidgets).length > 0) { + for (const widget of Object.values(domain.sharedWidgets)) { + if (widget.name === "newsletter-signup") { + delete domain.sharedWidgets[widget.name]; + } + } + } + if ( + domain.draftSharedWidgets && + Object.keys(domain.draftSharedWidgets).length > 0 + ) { + for (const widget of Object.values(domain.draftSharedWidgets)) { + if (widget.name === "newsletter-signup") { + delete domain.draftSharedWidgets[widget.name]; + } + } + } + domain.markModified("sharedWidgets"); + domain.markModified("draftSharedWidgets"); + await domain.save(); + console.log(`Migrated news letter signup for domain: ${domain.name}\n`); +}; + +const migrateSharedWidgets = async () => { + const domains = await Domain.find({}); + for (const domain of domains) { + try { + await unsetPaddingOnSharedWidgets(domain); + await removeNewsLetterSignupFromSharedWidgets(domain); + } catch (error) { + console.error( + `Error updating shared widgets for domain: ${domain.name}`, + ); + console.error(error); + } + } +}; + +(async () => { + await migratePages(); + await migrateSharedWidgets(); + mongoose.connection.close(); +})(); diff --git a/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/client-side-text-renderer.tsx b/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/client-side-text-renderer.tsx new file mode 100644 index 000000000..522188ff2 --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/client-side-text-renderer.tsx @@ -0,0 +1,7 @@ +"use client"; + +import { TextRenderer } from "@courselit/components-library"; + +export default function ClientSideTextRenderer({ json }: { json: any }) { + return ; +} diff --git a/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/layout.tsx b/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/layout.tsx new file mode 100644 index 000000000..2fe4b25b5 --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/layout.tsx @@ -0,0 +1,85 @@ +import type { Metadata, ResolvingMetadata } from "next"; +import { ReactNode } from "react"; +import { FetchBuilder } from "@courselit/utils"; +import { headers } from "next/headers"; +import { getAddressFromHeaders, getSiteInfo } from "@ui-lib/utils"; +import { Course } from "@courselit/common-models"; + +export async function generateMetadata( + { params }: { params: { id: string } }, + parent: ResolvingMetadata, +): Promise { + const address = getAddressFromHeaders(headers); + const [product, siteInfo] = await Promise.all([ + getProduct(params.id, address), + getSiteInfo(address), + ]); + + return { + title: `${product ? product.title : "Post not found"} | ${(await parent)?.title?.absolute}`, + openGraph: { + title: `${product?.title || "Post not found"} | ${(await parent)?.title?.absolute}`, + images: [ + { + url: + product?.featuredImage?.file || + siteInfo?.logo?.file || + "", + alt: + product?.featuredImage?.caption || + siteInfo?.logo?.caption || + "", + }, + ], + }, + twitter: { + title: `${product?.title || "Post not found"} | ${(await parent)?.title?.absolute}`, + images: [ + { + url: + product?.featuredImage?.file || + siteInfo?.logo?.file || + "", + alt: + product?.featuredImage?.caption || + siteInfo?.logo?.caption || + "", + }, + ], + }, + }; +} + +async function getProduct(id: string, address: string): Promise { + const query = ` + query ($id: String!) { + product: getCourse(id: $id) { + courseId + title + description + slug + featuredImage { + thumbnail + file + } + creatorName + updatedAt + } + } + `; + const fetch = new FetchBuilder() + .setUrl(`${address}/api/graph`) + .setPayload({ query, variables: { id } }) + .setIsGraphQLEndpoint(true) + .build(); + try { + const response = await fetch.exec(); + return response.product; + } catch (err: any) { + return null; + } +} + +export default function Layout({ children }: { children: ReactNode }) { + return children; +} diff --git a/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/page.tsx b/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/page.tsx new file mode 100644 index 000000000..b15983f70 --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/page.tsx @@ -0,0 +1,152 @@ +import { Course } from "@courselit/common-models"; +import { Caption, Header1, Section, Text1 } from "@courselit/page-primitives"; +import { formattedLocaleDate, getFullSiteSetup } from "@ui-lib/utils"; +import { getAddressFromHeaders } from "@ui-lib/utils"; +import { headers } from "next/headers"; +import { ProductWithAdminProps } from "@/hooks/use-product"; +import { FetchBuilder } from "@courselit/utils"; +import { Text2 } from "@courselit/page-primitives"; +import Link from "next/link"; +import { truncate } from "@ui-lib/utils"; +import Image from "next/image"; +import ClientSideTextRenderer from "./client-side-text-renderer"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from "@components/ui/breadcrumb"; + +export default async function ProductPage({ + params, +}: { + params: { slug: string; id: string }; + course: Course; +}) { + const address = getAddressFromHeaders(headers); + const [product, siteInfo] = await Promise.all([ + getProduct(address, params.id), + getFullSiteSetup(address), + ]); + if (!siteInfo) { + return null; + } + + const { theme } = siteInfo; + + if (!product) { + return
Post not found
; + } + + return ( +
+
+ + + + + + Blog + + + + + + + + {truncate(product?.title, 20)} + + + + + + {product?.title} +
+ {product?.user?.name +
+ + {truncate(product?.user?.name, 50)} + + + · {formattedLocaleDate(product?.updatedAt, "long")} + +
+
+ {product?.featuredImage && ( +
+ {product.featuredImage.caption +
+ )} + {product?.description && ( + + + + )} +
+
+ ); +} + +export const getProduct = async ( + backend: string, + id: string, +): Promise => { + const query = ` + query ($id: String!) { + course: getCourse(id: $id) { + title + description + type + slug + courseId + featuredImage { + file + thumbnail + caption + }, + updatedAt + user { + name + avatar { + file + thumbnail + caption + } + } + } + } + `; + const fetch = new FetchBuilder() + .setUrl(`${backend}/api/graph`) + .setPayload({ query, variables: { id } }) + .setIsGraphQLEndpoint(true) + .build(); + + try { + const response = await fetch.exec(); + return response.course; + } catch (e: any) { + console.log("Error fetching product", e.message); // eslint-disable-line no-console + } +}; diff --git a/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/post.tsx.notused b/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/post.tsx.notused new file mode 100644 index 000000000..ed877b346 --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/blog/[slug]/[id]/post.tsx.notused @@ -0,0 +1,78 @@ +"use client"; + +import { Breadcrumbs, TextRenderer } from "@courselit/components-library"; +import { + Header1, + Caption, + Text1, + Text2, +} from "@courselit/page-primitives"; +import Image from "next/image"; +import { formattedLocaleDate, truncate } from "@ui-lib/utils"; +import useProduct from "@/hooks/use-product"; +import { AddressContext, ThemeContext } from "@components/contexts"; +import { useContext } from "react"; +import Link from "next/link"; +import { Image as CourselitImage } from "@courselit/components-library"; + +export default function Post({ courseId }: { courseId: string }) { + const address = useContext(AddressContext); + const { theme } = useContext(ThemeContext); + const { product: post, loaded } = useProduct(courseId, address); + + if (!loaded && !post) { + return null; + } + + if (loaded && !post) { + return Post not found; + } + + return ( + <> + + + Blog + + {truncate(post?.title, 20)} + + {post?.title} +
+ {post?.user?.name +
+ {post?.creatorName} + + · {formattedLocaleDate(post?.updatedAt, "long")} + +
+
+ {post?.featuredImage && ( +
+ +
+ )} + {post?.description && ( + + + + )} + + ); +} diff --git a/apps/web/app/(with-contexts)/(with-layout)/blog/blogs-list.tsx b/apps/web/app/(with-contexts)/(with-layout)/blog/blogs-list.tsx index fb8ac0261..9aa869ef6 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/blog/blogs-list.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/blog/blogs-list.tsx @@ -1,37 +1,58 @@ "use client"; -import { useMemo, useState } from "react"; -import { SkeletonCard } from "./skeleton-card"; +import { useContext, useMemo } from "react"; import { BlogContentCard } from "./content-card"; import { PaginationControls } from "@components/public/pagination"; import { Constants, Course } from "@courselit/common-models"; import { useProducts } from "@/hooks/use-products"; +import { ProductCardSkeleton } from "@courselit/page-blocks"; +import { ThemeContext } from "@components/contexts"; +import { BookOpen } from "lucide-react"; +import { Button, Subheader1 } from "@courselit/page-primitives"; const ITEMS_PER_PAGE = 9; export function BlogsList({ + page, itemsPerPage = ITEMS_PER_PAGE, + onPageChange, }: { + page: number; itemsPerPage?: number; + onPageChange: (page: number) => void; }) { - const [currentPage, setCurrentPage] = useState(1); + const { theme: uiTheme } = useContext(ThemeContext); + const { theme } = uiTheme; + const filters = useMemo( () => [Constants.CourseType.BLOG.toUpperCase()], [], ); const { products, loading, totalPages } = useProducts( - currentPage, + page, itemsPerPage, filters, true, ); + if (!loading && totalPages && products.length === 0) { + return ( +
+ + This page is empty. + +
+ ); + } + return (
{loading ? Array.from({ length: ITEMS_PER_PAGE }).map((_, index) => ( - + )) : products.map((product: Course) => (
); diff --git a/apps/web/app/(with-contexts)/(with-layout)/blog/content-card.tsx b/apps/web/app/(with-contexts)/(with-layout)/blog/content-card.tsx index 8e1273f48..41431718f 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/blog/content-card.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/blog/content-card.tsx @@ -1,40 +1,61 @@ +import { ThemeContext } from "@components/contexts"; import { Course } from "@courselit/common-models"; -import { - ContentCard, - ContentCardContent, - ContentCardHeader, - ContentCardImage, - Image, -} from "@courselit/components-library"; +import { Image } from "@courselit/components-library"; +import { useContext } from "react"; import { truncate } from "@ui-lib/utils"; +import { + PageCard, + PageCardContent, + PageCardHeader, + PageCardImage, + Subheader1, +} from "@courselit/page-primitives"; +import Link from "next/link"; export function BlogContentCard({ product }: { product: Course }) { + const { theme: uiTheme } = useContext(ThemeContext); + const { theme } = uiTheme; + return ( - - - - - {truncate(product.title, 32)} - -
-
- {product.user?.name - - {truncate(product.user?.name || "Unnamed", 30)} - + + + + + + {product.title} + +
+
+ {product.user?.name + + {truncate(product.user?.name || "Unnamed", 20)} + +
-
- - + + + ); } diff --git a/apps/web/app/(with-contexts)/(with-layout)/blog/page.tsx b/apps/web/app/(with-contexts)/(with-layout)/blog/page.tsx index f535bd119..2565c10e8 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/blog/page.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/blog/page.tsx @@ -1,14 +1,33 @@ -import { Suspense } from "react"; +"use client"; + +import { Suspense, useCallback, useContext } from "react"; import { BlogsList } from "./blogs-list"; import { PAGE_HEADER_ALL_POSTS } from "@ui-config/strings"; +import { ThemeContext } from "@components/contexts"; +import { Header1, Section } from "@courselit/page-primitives"; +import { useRouter, useSearchParams } from "next/navigation"; export default function BlogsPage() { + const searchParams = useSearchParams(); + const page = parseInt(searchParams?.get("page") || "1"); + const router = useRouter(); + const { theme } = useContext(ThemeContext); + + const handlePageChange = useCallback( + (value: number) => { + router.push(`/blog?page=${value}`); + }, + [router], + ); + return ( -
-

{PAGE_HEADER_ALL_POSTS}

- Loading...
}> - - -
+
+
+ {PAGE_HEADER_ALL_POSTS} + Loading...
}> + + +
+ ); } diff --git a/apps/web/app/(with-contexts)/(with-layout)/checkout/page.tsx b/apps/web/app/(with-contexts)/(with-layout)/checkout/page.tsx index c7c64fd80..10816ebf9 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/checkout/page.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/checkout/page.tsx @@ -1,152 +1,20 @@ "use client"; -import { AddressContext } from "@components/contexts"; -import Checkout, { Product } from "@components/public/payments/checkout"; -import { Constants, PaymentPlan } from "@courselit/common-models"; -import { useToast } from "@courselit/components-library"; -import { FetchBuilder } from "@courselit/utils"; -import { TOAST_TITLE_ERROR } from "@ui-config/strings"; -import { useSearchParams } from "next/navigation"; -import { useCallback, useContext, useEffect, useState } from "react"; - -const { MembershipEntityType } = Constants; +import { ThemeContext } from "@components/contexts"; +import { Header1, Section } from "@courselit/page-primitives"; +import { useContext } from "react"; +import ProductCheckout from "./product"; export default function CheckoutPage() { - const address = useContext(AddressContext); - const searchParams = useSearchParams(); - const entityId = searchParams?.get("id"); - const entityType = searchParams?.get("type"); - const { toast } = useToast(); - - const [product, setProduct] = useState(null); - const [paymentPlans, setPaymentPlans] = useState([]); - - const getProduct = useCallback(async () => { - const query = ` - query ($id: String!) { - course: getCourse(id: $id) { - courseId - title - slug - featuredImage { - thumbnail - file - } - paymentPlans { - planId - name - type - oneTimeAmount - emiAmount - emiTotalInstallments - subscriptionMonthlyAmount - subscriptionYearlyAmount - } - defaultPaymentPlan - } - } - `; - const fetch = new FetchBuilder() - .setUrl(`${address.backend}/api/graph`) - .setPayload({ query, variables: { id: entityId } }) - .setIsGraphQLEndpoint(true) - .build(); - try { - const response = await fetch.exec(); - if (response.course) { - setProduct({ - id: response.course.courseId, - name: response.course.title, - slug: response.course.slug, - featuredImage: response.course.featuredImage?.file, - type: MembershipEntityType.COURSE, - }); - setPaymentPlans([...response.course.paymentPlans]); - } else { - toast({ - title: TOAST_TITLE_ERROR, - description: "Course not found", - }); - } - } catch (err: any) { - toast({ - title: TOAST_TITLE_ERROR, - description: err.message, - }); - } finally { - } - }, [address.backend, entityId, toast]); - - const getCommunity = useCallback(async () => { - const query = ` - query ($id: String!) { - community: getCommunity(id: $id) { - communityId - name - paymentPlans { - planId - name - type - oneTimeAmount - emiAmount - emiTotalInstallments - subscriptionMonthlyAmount - subscriptionYearlyAmount - } - featuredImage { - thumbnail - file - } - autoAcceptMembers - joiningReasonText - } - } - `; - const fetch = new FetchBuilder() - .setUrl(`${address.backend}/api/graph`) - .setPayload({ query, variables: { id: entityId } }) - .setIsGraphQLEndpoint(true) - .build(); - try { - const response = await fetch.exec(); - if (response.community) { - setProduct({ - id: response.community.communityId, - name: response.community.name, - type: MembershipEntityType.COMMUNITY, - featuredImage: response.community.featuredImage?.file, - joiningReasonText: response.community.joiningReasonText, - autoAcceptMembers: response.community.autoAcceptMembers, - }); - setPaymentPlans([...response.community.paymentPlans]); - } else { - toast({ - title: TOAST_TITLE_ERROR, - description: "Community not found", - }); - } - } catch (err: any) { - toast({ - title: TOAST_TITLE_ERROR, - description: err.message, - }); - } finally { - } - }, [address.backend, entityId, toast]); - - useEffect(() => { - if (entityId && entityType) { - if (entityType === MembershipEntityType.COURSE) { - getProduct(); - } else if (entityType === MembershipEntityType.COMMUNITY) { - getCommunity(); - } - } - }, [entityId, entityType, getProduct, getCommunity]); - - if (!product) { - return null; - } - - return ; + const { theme } = useContext(ThemeContext); + return ( +
+
+ + Checkout + + +
+
+ ); } diff --git a/apps/web/app/(with-contexts)/(with-layout)/checkout/product.tsx b/apps/web/app/(with-contexts)/(with-layout)/checkout/product.tsx new file mode 100644 index 000000000..40079f4c8 --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/checkout/product.tsx @@ -0,0 +1,152 @@ +"use client"; + +import { AddressContext } from "@components/contexts"; +import Checkout, { Product } from "@components/public/payments/checkout"; +import { Constants, PaymentPlan } from "@courselit/common-models"; +import { useToast } from "@courselit/components-library"; +import { FetchBuilder } from "@courselit/utils"; +import { TOAST_TITLE_ERROR } from "@ui-config/strings"; +import { useSearchParams } from "next/navigation"; +import { useCallback, useContext, useEffect, useState } from "react"; + +const { MembershipEntityType } = Constants; + +export default function ProductCheckout() { + const address = useContext(AddressContext); + const searchParams = useSearchParams(); + const entityId = searchParams?.get("id"); + const entityType = searchParams?.get("type"); + const { toast } = useToast(); + + const [product, setProduct] = useState(null); + const [paymentPlans, setPaymentPlans] = useState([]); + + const getProduct = useCallback(async () => { + const query = ` + query ($id: String!) { + course: getCourse(id: $id) { + courseId + title + slug + featuredImage { + thumbnail + file + } + paymentPlans { + planId + name + type + oneTimeAmount + emiAmount + emiTotalInstallments + subscriptionMonthlyAmount + subscriptionYearlyAmount + } + defaultPaymentPlan + } + } + `; + const fetch = new FetchBuilder() + .setUrl(`${address.backend}/api/graph`) + .setPayload({ query, variables: { id: entityId } }) + .setIsGraphQLEndpoint(true) + .build(); + try { + const response = await fetch.exec(); + if (response.course) { + setProduct({ + id: response.course.courseId, + name: response.course.title, + slug: response.course.slug, + featuredImage: response.course.featuredImage?.file, + type: MembershipEntityType.COURSE, + }); + setPaymentPlans([...response.course.paymentPlans]); + } else { + toast({ + title: TOAST_TITLE_ERROR, + description: "Course not found", + }); + } + } catch (err: any) { + toast({ + title: TOAST_TITLE_ERROR, + description: err.message, + }); + } finally { + } + }, [address.backend, entityId, toast]); + + const getCommunity = useCallback(async () => { + const query = ` + query ($id: String!) { + community: getCommunity(id: $id) { + communityId + name + paymentPlans { + planId + name + type + oneTimeAmount + emiAmount + emiTotalInstallments + subscriptionMonthlyAmount + subscriptionYearlyAmount + } + featuredImage { + thumbnail + file + } + autoAcceptMembers + joiningReasonText + } + } + `; + const fetch = new FetchBuilder() + .setUrl(`${address.backend}/api/graph`) + .setPayload({ query, variables: { id: entityId } }) + .setIsGraphQLEndpoint(true) + .build(); + try { + const response = await fetch.exec(); + if (response.community) { + setProduct({ + id: response.community.communityId, + name: response.community.name, + type: MembershipEntityType.COMMUNITY, + featuredImage: response.community.featuredImage?.file, + joiningReasonText: response.community.joiningReasonText, + autoAcceptMembers: response.community.autoAcceptMembers, + }); + setPaymentPlans([...response.community.paymentPlans]); + } else { + toast({ + title: TOAST_TITLE_ERROR, + description: "Community not found", + }); + } + } catch (err: any) { + toast({ + title: TOAST_TITLE_ERROR, + description: err.message, + }); + } finally { + } + }, [address.backend, entityId, toast]); + + useEffect(() => { + if (entityId && entityType) { + if (entityType === MembershipEntityType.COURSE) { + getProduct(); + } else if (entityType === MembershipEntityType.COMMUNITY) { + getCommunity(); + } + } + }, [entityId, entityType, getProduct, getCommunity]); + + if (!product) { + return null; + } + + return ; +} diff --git a/apps/web/app/(with-contexts)/(with-layout)/communities/communities-list.tsx b/apps/web/app/(with-contexts)/(with-layout)/communities/communities-list.tsx index 612ace945..db857777c 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/communities/communities-list.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/communities/communities-list.tsx @@ -5,8 +5,10 @@ import { CommunityContentCard } from "./content-card"; import { PaginationControls } from "@components/public/pagination"; import { Community } from "@courselit/common-models"; import { Users } from "lucide-react"; -import { Button } from "@components/ui/button"; import { SkeletonCard } from "@components/skeleton-card"; +import { useContext } from "react"; +import { ThemeContext } from "@components/contexts"; +import { Button, Header3, Text2 } from "@courselit/page-primitives"; const ITEMS_PER_PAGE = 9; @@ -25,18 +27,17 @@ export function CommunitiesList({ page, itemsPerPage, ); + const { theme } = useContext(ThemeContext); if (!loading && totalPages === 0) { return (
-

- No Communities Found -

-

+ No Communities Found + {publicView ? "The team " : "You have "} not added any communities yet. -

+
); } @@ -45,10 +46,12 @@ export function CommunitiesList({ return (
-

- This page is empty. -

-
diff --git a/apps/web/app/(with-contexts)/(with-layout)/communities/content-card.tsx b/apps/web/app/(with-contexts)/(with-layout)/communities/content-card.tsx index a96070450..e2d4dbc34 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/communities/content-card.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/communities/content-card.tsx @@ -1,8 +1,15 @@ -import Image from "next/image"; -import { Card, CardContent } from "@/components/ui/card"; -import { CheckCircle, CircleDashed, Users } from "lucide-react"; +import { ThemeContext } from "@components/contexts"; import { Community } from "@courselit/common-models"; -import { Link } from "@courselit/components-library"; +import { useContext } from "react"; +import { + PageCard, + PageCardContent, + PageCardHeader, + PageCardImage, + Text2, +} from "@courselit/page-primitives"; +import Link from "next/link"; +import { CheckCircle, CircleDashed, Users } from "lucide-react"; import { Tooltip, TooltipContent, @@ -17,38 +24,42 @@ export function CommunityContentCard({ community: Community; publicView?: boolean; }) { + const { theme: uiTheme } = useContext(ThemeContext); + const { theme } = uiTheme; + return ( - - -
- {community.name} -
- -

+ + + + + {community.name} -

-
-
+ +
+
- + {community.membersCount.toLocaleString()}{" "} members - +
{!publicView && ( @@ -69,8 +80,8 @@ export function CommunityContentCard({ )}
- - - + + + ); } diff --git a/apps/web/app/(with-contexts)/(with-layout)/communities/page.tsx b/apps/web/app/(with-contexts)/(with-layout)/communities/page.tsx index 1807a03bd..a4a77c2a7 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/communities/page.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/communities/page.tsx @@ -1,13 +1,16 @@ "use client"; -import { useCallback } from "react"; +import { useCallback, useContext } from "react"; import { CommunitiesList } from "./communities-list"; import { useRouter, useSearchParams } from "next/navigation"; +import { ThemeContext } from "@components/contexts"; +import { Header1, Section } from "@courselit/page-primitives"; export default function CommunitiesPage() { const searchParams = useSearchParams(); const page = parseInt(searchParams?.get("page") || "1"); const router = useRouter(); + const { theme } = useContext(ThemeContext); const handlePageChange = useCallback( (value: number) => { @@ -17,13 +20,15 @@ export default function CommunitiesPage() { ); return ( -
-

Communities

- -
+
+
+ Communities + +
+
); } diff --git a/apps/web/app/(with-contexts)/(with-layout)/home-page-layout.tsx b/apps/web/app/(with-contexts)/(with-layout)/home-page-layout.tsx index c92bc8b4d..719acc146 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/home-page-layout.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/home-page-layout.tsx @@ -6,57 +6,51 @@ import { ServerConfigContext, SiteInfoContext, TypefacesContext, + ThemeContext, } from "@components/contexts"; -import { MasterLayout } from "@components/public/base-layout"; +import { BaseLayout } from "@components/public/base-layout"; import { Profile } from "@courselit/common-models"; -import { getPage } from "@ui-lib/utils"; -import { useContext, useEffect, useState } from "react"; +import { getFullSiteSetup } from "@ui-lib/utils"; +import { useContext } from "react"; export default function HomepageLayout({ children, + siteInfo, }: { children: React.ReactNode; + siteInfo: Awaited>; }) { const address = useContext(AddressContext); const siteinfo = useContext(SiteInfoContext); const typefaces = useContext(TypefacesContext); - const [page, setPage] = useState(null); const config = useContext(ServerConfigContext); const { profile } = useContext(ProfileContext); - - useEffect(() => { - if (address.backend) { - getPage(address.backend).then(setPage); - } - }, [address]); - - if (!page) { - return null; - } + const { theme } = useContext(ThemeContext); return ( - {}} + theme={theme} state={{ config: config, siteinfo, address: address, profile: profile as Profile, - auth: { - guest: profile ? false : true, - checked: profile ? true : false, - }, + auth: profile?.email + ? { + guest: false, + checked: true, + } + : { + guest: true, + checked: true, + }, networkAction: false, - theme: { - name: "", - active: false, - styles: {}, - }, + theme, typefaces, message: { message: "", @@ -65,7 +59,7 @@ export default function HomepageLayout({ }, }} > -
{children}
-
+ {children} + ); } diff --git a/apps/web/app/(with-contexts)/(with-layout)/layout.tsx b/apps/web/app/(with-contexts)/(with-layout)/layout.tsx index 18ec3fc90..2e02da238 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/layout.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/layout.tsx @@ -1,17 +1,27 @@ import { auth } from "@/auth"; import { SessionProvider } from "next-auth/react"; import HomepageLayout from "./home-page-layout"; +import { headers } from "next/headers"; +import { getAddressFromHeaders, getFullSiteSetup } from "@ui-lib/utils"; export default async function Layout({ children, }: { children: React.ReactNode; }) { - const session = await auth(); + const address = getAddressFromHeaders(headers); + const [siteInfo, session] = await Promise.all([ + getFullSiteSetup(address), + auth(), + ]); + + if (!siteInfo) { + return null; + } return ( - {children} + {children} ); } diff --git a/apps/web/app/(with-contexts)/(with-layout)/login/login-form.tsx b/apps/web/app/(with-contexts)/(with-layout)/login/login-form.tsx new file mode 100644 index 000000000..119224591 --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/login/login-form.tsx @@ -0,0 +1,223 @@ +"use client"; + +import { ThemeContext } from "@components/contexts"; +import { + Button, + Caption, + Input, + Section, + Text1, + Text2, + Link as PageLink, +} from "@courselit/page-primitives"; +import { useContext, useState } from "react"; +import { FormEvent } from "react"; +import { signIn } from "next-auth/react"; +import { + Form, + // FormField, + // FormSubmit, + useToast, +} from "@courselit/components-library"; +import { + BTN_LOGIN, + BTN_LOGIN_GET_CODE, + BTN_LOGIN_CODE_INTIMATION, + LOGIN_NO_CODE, + BTN_LOGIN_NO_CODE, + LOGIN_FORM_LABEL, + LOGIN_FORM_DISCLAIMER, + LOADING, + TOAST_TITLE_ERROR, +} from "@/ui-config/strings"; +import Link from "next/link"; +import { TriangleAlert } from "lucide-react"; +import { useRouter } from "next/navigation"; + +export default function LoginForm({ redirectTo }: { redirectTo?: string }) { + const { theme } = useContext(ThemeContext); + const [showCode, setShowCode] = useState(false); + const [email, setEmail] = useState(""); + const [code, setCode] = useState(""); + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); + const { toast } = useToast(); + const router = useRouter(); + + const requestCode = async function (e: FormEvent) { + e.preventDefault(); + const url = `/api/auth/code/generate?email=${encodeURIComponent( + email, + )}`; + try { + setLoading(true); + const response = await fetch(url); + const resp = await response.json(); + if (response.ok) { + setShowCode(true); + } else { + toast({ + title: TOAST_TITLE_ERROR, + description: resp.error, + variant: "destructive", + }); + } + } finally { + setLoading(false); + } + }; + + const signInUser = async function (e: FormEvent) { + e.preventDefault(); + try { + setLoading(true); + const response = await signIn("credentials", { + email, + code, + redirect: false, + }); + if (response?.error) { + setError(`Can't sign you in at this time`); + } else { + // toast({ + // title: TOAST_TITLE_SUCCESS, + // description: LOGIN_SUCCESS, + // }); + // router.replace(redirectTo || "/dashboard/my-content"); + window.location.href = redirectTo || "/dashboard/my-content"; + } + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+
+ {error && ( +
+ +
+ {error} +
+
+ )} + {!showCode && ( +
+ + {LOGIN_FORM_LABEL} + +
+ + setEmail(e.target.value) + } + theme={theme.theme} + /> + + + {LOGIN_FORM_DISCLAIMER} + + + Terms + + + +
+ {/* */} + +
+
+
+ )} + {showCode && ( +
+ + {BTN_LOGIN_CODE_INTIMATION}{" "} + {email} + +
+ + setCode(e.target.value) + } + theme={theme.theme} + /> +
+ {/* */} + +
+
+
+ + {LOGIN_NO_CODE} + + +
+
+ )} +
+
+
+
+ ); +} diff --git a/apps/web/app/(with-contexts)/(with-layout)/login/page.tsx b/apps/web/app/(with-contexts)/(with-layout)/login/page.tsx new file mode 100644 index 000000000..a73e1d3b2 --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/login/page.tsx @@ -0,0 +1,22 @@ +import { auth } from "@/auth"; +import { redirect } from "next/navigation"; +import LoginForm from "./login-form"; + +export default async function LoginPage({ + searchParams, +}: { + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +}) { + const session = await auth(); + const redirectTo = (await searchParams).redirect; + + if (session) { + redirect( + typeof redirectTo === "string" + ? redirectTo + : "/dashboard/my-content", + ); + } + + return ; +} diff --git a/apps/web/app/(with-contexts)/(with-layout)/p/[id]/client-side-page.tsx b/apps/web/app/(with-contexts)/(with-layout)/p/[id]/client-side-page.tsx new file mode 100644 index 000000000..8c6351558 --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/p/[id]/client-side-page.tsx @@ -0,0 +1,72 @@ +"use client"; + +import { useContext } from "react"; +import { + AddressContext, + ServerConfigContext, + TypefacesContext, +} from "@components/contexts"; +import { ProfileContext } from "@components/contexts"; +import { BaseLayout } from "@components/public/base-layout"; +import { Profile } from "@courselit/common-models"; + +export default function ClientSidePage({ + page, + siteinfo, + theme, +}: { + page; + siteinfo; + theme; +}) { + const typefaces = useContext(TypefacesContext); + const config = useContext(ServerConfigContext); + const { profile } = useContext(ProfileContext); + const address = useContext(AddressContext); + + if (!page) { + return null; + } + + const layoutWithoutHeaderFooter = page?.layout + ?.filter((layout: any) => !["header", "footer"].includes(layout.name)) + ?.map((layout: any) => ({ + ...layout, + settings: layout.settings || {}, + })); + + return ( + {}} + theme={theme} + state={{ + config: config, + siteinfo, + address, + profile: profile as Profile, + auth: profile.email + ? { + guest: false, + checked: true, + } + : { + guest: true, + checked: true, + }, + networkAction: false, + theme, + typefaces, + message: { + message: "", + open: false, + action: null, + }, + }} + /> + ); +} diff --git a/apps/web/app/(with-contexts)/(with-layout)/p/[id]/page.tsx b/apps/web/app/(with-contexts)/(with-layout)/p/[id]/page.tsx new file mode 100644 index 000000000..f6a64302d --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/p/[id]/page.tsx @@ -0,0 +1,86 @@ +import { getFullSiteSetup, getPage } from "@ui-lib/utils"; +import { getAddressFromHeaders } from "@/ui-lib/utils"; +import ClientSidePage from "./client-side-page"; +import { headers } from "next/headers"; +import type { Metadata, ResolvingMetadata } from "next"; +import { Media } from "@courselit/common-models"; + +type Props = { + params: { + id: string; + }; +}; + +export async function generateMetadata( + { params }: Props, + parent: ResolvingMetadata, +): Promise { + const address = getAddressFromHeaders(headers); + const [siteInfo, page] = await Promise.all([ + getFullSiteSetup(address), + getPage(address, params.id), + ]); + if (!siteInfo) { + return { + title: `${(await parent)?.title?.absolute}`, + }; + } + + const title = page.title || page.pageData?.title || page.name; + const socialImage: Media | undefined = + page.socialImage || + (page.pageData?.featuredImage as Media) || + siteInfo.settings.logo; + const description = page.description; + + return { + generator: "CourseLit", + title: `${title} | ${(await parent)?.title?.absolute}`, + description, + openGraph: { + title: `${title} | ${(await parent)?.title?.absolute}`, + description, + images: [ + { + url: socialImage?.file || "", + alt: socialImage?.caption || "", + }, + ], + }, + twitter: { + title: `${title} | ${(await parent)?.title?.absolute}`, + description, + images: [ + { + url: socialImage?.file || "", + alt: socialImage?.caption || "", + }, + ], + }, + robots: { + index: page.robotsAllowed, + }, + icons: { + icon: siteInfo.settings.logo?.file || "/favicon.ico", + }, + }; +} + +export default async function Page({ params }: Props) { + const address = getAddressFromHeaders(headers); + const [siteInfo, page] = await Promise.all([ + getFullSiteSetup(address), + getPage(address, params.id), + ]); + if (!siteInfo) { + return null; + } + + return ( + + ); +} diff --git a/apps/web/app/(with-contexts)/(with-layout)/page.tsx b/apps/web/app/(with-contexts)/(with-layout)/page.tsx new file mode 100644 index 000000000..e531f9721 --- /dev/null +++ b/apps/web/app/(with-contexts)/(with-layout)/page.tsx @@ -0,0 +1,78 @@ +import { getFullSiteSetup } from "@ui-lib/utils"; +import { getAddressFromHeaders } from "@/ui-lib/utils"; +import ClientSidePage from "./p/[id]/client-side-page"; +import { headers } from "next/headers"; +import type { Metadata, ResolvingMetadata } from "next"; + +type Props = { + params: { + id: string; + }; +}; + +export async function generateMetadata( + { params }: Props, + parent: ResolvingMetadata, +): Promise { + const address = getAddressFromHeaders(headers); + const siteInfo = await getFullSiteSetup(address, "homepage"); + if (!siteInfo) { + return { + title: "CourseLit", + }; + } + + const page = siteInfo.page; + + const title = page.title || siteInfo.settings.title; + const socialImage = page.socialImage || siteInfo.settings.logo; + const description = page.description || siteInfo.settings.subtitle; + + return { + generator: "CourseLit", + title, + description, + openGraph: { + title, + description, + images: [ + { + url: socialImage?.file || "", + alt: socialImage?.caption || "", + }, + ], + }, + twitter: { + title, + description, + images: [ + { + url: socialImage?.file || "", + alt: socialImage?.caption || "", + }, + ], + }, + robots: { + index: page.robotsAllowed, + }, + icons: { + icon: siteInfo.settings.logo?.file || "/favicon.ico", + }, + }; +} + +export default async function Page() { + const address = getAddressFromHeaders(headers); + const siteInfo = await getFullSiteSetup(address, "homepage"); + if (!siteInfo) { + return null; + } + + return ( + + ); +} diff --git a/apps/web/app/(with-contexts)/(with-layout)/products/content-card.tsx b/apps/web/app/(with-contexts)/(with-layout)/products/content-card.tsx deleted file mode 100644 index 6aae0864f..000000000 --- a/apps/web/app/(with-contexts)/(with-layout)/products/content-card.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import Image from "next/image"; -import { Card, CardContent } from "@/components/ui/card"; -import { Course } from "@courselit/common-models"; -import { getSymbolFromCurrency, Link } from "@courselit/components-library"; -import { getPlanPrice } from "@courselit/utils"; -import { useContext } from "react"; -import { SiteInfoContext } from "@components/contexts"; -import { Badge } from "@components/ui/badge"; -import { truncate } from "@ui-lib/utils"; - -export function ProductContentCard({ product }: { product: Course }) { - const siteinfo = useContext(SiteInfoContext); - const defaultPlan = product.paymentPlans?.filter( - (plan) => plan.planId === product.defaultPaymentPlan, - )[0]; - const { amount, period } = getPlanPrice(defaultPlan); - - return ( - - -
- {product.title} -
- -

- {product.title} -

-
-
- {product.user?.name - - {truncate(product.user?.name || "Unnamed", 20)} - -
- - {getSymbolFromCurrency( - siteinfo.currencyISOCode || "USD", - )} - {amount.toFixed(2)} - {period} - -
-
-
- - ); -} diff --git a/apps/web/app/(with-contexts)/(with-layout)/products/empty-state.tsx b/apps/web/app/(with-contexts)/(with-layout)/products/empty-state.tsx index a2cf6199b..209aff451 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/products/empty-state.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/products/empty-state.tsx @@ -1,14 +1,21 @@ +import { ThemeContext } from "@components/contexts"; +import { useContext } from "react"; import { BookOpen } from "lucide-react"; +import { Subheader1, Text2 } from "@courselit/page-primitives"; export function EmptyState({ publicView = true }: { publicView?: boolean }) { + const { theme } = useContext(ThemeContext); + return (
-

No Products Found

-

- {publicView ? "The team has " : "You have "} not added any - products yet. -

+ No Products Found +
+ + {publicView ? "The team has " : "You have "} not added any + products yet. + +
); } diff --git a/apps/web/app/(with-contexts)/(with-layout)/products/page.tsx b/apps/web/app/(with-contexts)/(with-layout)/products/page.tsx index 53dc837a0..e9710be12 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/products/page.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/products/page.tsx @@ -1,13 +1,16 @@ "use client"; -import { useCallback } from "react"; +import { useCallback, useContext } from "react"; import { ProductsList } from "./products-list"; import { useRouter, useSearchParams } from "next/navigation"; +import { Header1, Section } from "@courselit/page-primitives"; +import { ThemeContext } from "@components/contexts"; export default function CoursesPage() { const searchParams = useSearchParams(); const page = parseInt(searchParams?.get("page") || "1"); const router = useRouter(); + const { theme } = useContext(ThemeContext); const handlePageChange = useCallback( (value: number) => { @@ -17,9 +20,15 @@ export default function CoursesPage() { ); return ( -
-

Products

- -
+
+
+ Products + +
+
); } diff --git a/apps/web/app/(with-contexts)/(with-layout)/products/products-list.tsx b/apps/web/app/(with-contexts)/(with-layout)/products/products-list.tsx index 97d45c62e..5f2980965 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/products/products-list.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/products/products-list.tsx @@ -2,21 +2,25 @@ import { useMemo, useContext } from "react"; import { PaginationControls } from "@components/public/pagination"; -import { Constants, Course } from "@courselit/common-models"; +import { Constants, Course, SiteInfo } from "@courselit/common-models"; import { useProducts } from "@/hooks/use-products"; import { BookOpen } from "lucide-react"; import { EmptyState } from "./empty-state"; -import { Button } from "@components/ui/button"; -import { SkeletonCard, ProductCard } from "@courselit/components-library"; +import { ProductCard, ProductCardSkeleton } from "@courselit/page-blocks"; import { SiteInfoContext } from "@components/contexts"; -import { truncate } from "@ui-lib/utils"; +import { getPlanPrice, truncate } from "@ui-lib/utils"; +import { Button, Subheader1 } from "@courselit/page-primitives"; +import { getSymbolFromCurrency } from "@courselit/components-library"; +import { ThemeStyle } from "@courselit/page-models"; const ITEMS_PER_PAGE = 9; export function ProductsList({ + theme, page, itemsPerPage = ITEMS_PER_PAGE, onPageChange, }: { + theme: ThemeStyle; page: number; itemsPerPage?: number; onPageChange: (page: number) => void; @@ -34,18 +38,22 @@ export function ProductsList({ true, ); + // return ( + //
+ // + //
+ // ); + if (!loading && totalPages === 0) { return ; } if (!loading && totalPages && products.length === 0) { return ( -
- -

- This page is empty. -

-
@@ -57,15 +65,23 @@ export function ProductsList({
{loading ? Array.from({ length: ITEMS_PER_PAGE }).map((_, index) => ( - + )) : products.map((product: Course) => ( ))}
@@ -77,3 +93,18 @@ export function ProductsList({
); } + +function getBadgeText(course: Course, siteinfo: SiteInfo) { + const defaultPlan = course.paymentPlans?.filter( + (plan) => plan.planId === course.defaultPaymentPlan, + )[0]; + const { amount, period } = getPlanPrice(defaultPlan); + + return ( + <> + {getSymbolFromCurrency(siteinfo.currencyISOCode || "USD")} + {amount.toFixed(2)} + {period} + + ); +} diff --git a/apps/web/app/(with-contexts)/dashboard/(sidebar)/layout.tsx b/apps/web/app/(with-contexts)/dashboard/(sidebar)/layout.tsx index 1cc679894..4c3524b63 100644 --- a/apps/web/app/(with-contexts)/dashboard/(sidebar)/layout.tsx +++ b/apps/web/app/(with-contexts)/dashboard/(sidebar)/layout.tsx @@ -1,15 +1,31 @@ +"use client"; + import { AppSidebar } from "@components/admin/dashboard-skeleton/app-sidebar"; import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; +import { ThemeContext } from "@components/contexts"; +import { themes } from "@courselit/page-primitives"; +import { Theme } from "@courselit/page-models"; export default async function Layout({ children, }: { children: React.ReactNode; }) { + const classicTheme = themes.find((theme) => theme.id === "classic"); + const theme: Theme = { + id: "classic", + name: "Classic", + theme: classicTheme!.theme, + }; + return ( - {children} + + {} }}> + {children} + + ); } diff --git a/apps/web/app/(with-contexts)/dashboard/(sidebar)/my-content/page.tsx b/apps/web/app/(with-contexts)/dashboard/(sidebar)/my-content/page.tsx index 7d3f0e54e..937ce1bac 100644 --- a/apps/web/app/(with-contexts)/dashboard/(sidebar)/my-content/page.tsx +++ b/apps/web/app/(with-contexts)/dashboard/(sidebar)/my-content/page.tsx @@ -2,7 +2,11 @@ import { useState, useEffect, useContext } from "react"; import type { ContentItem } from "@/components/admin/my-content/content"; -import { AddressContext, ProfileContext } from "@components/contexts"; +import { + AddressContext, + ProfileContext, + ThemeContext, +} from "@components/contexts"; import { MY_CONTENT_HEADER } from "@ui-config/strings"; import DashboardContent from "@components/admin/dashboard-content"; import { FetchBuilder } from "@courselit/utils"; @@ -10,8 +14,8 @@ import { Constants, MembershipEntityType } from "@courselit/common-models"; import { MyContentCard } from "@components/admin/my-content/content-card"; import { BookOpen, Users } from "lucide-react"; import Link from "next/link"; -import { Button } from "@components/ui/button"; import { SkeletonCard } from "@components/skeleton-card"; +import { Button } from "@courselit/page-primitives"; function ContentGrid({ items, @@ -45,6 +49,7 @@ export default function Page() { const [loading, setLoading] = useState(true); const { profile } = useContext(ProfileContext); const address = useContext(AddressContext); + const { theme } = useContext(ThemeContext); useEffect(() => { const getUserContent = async () => { @@ -111,13 +116,13 @@ export default function Page() {

{type === Constants.MembershipEntityType.COURSE ? ( - ) : ( - diff --git a/apps/web/app/(with-contexts)/dashboard/page/[id]/page.tsx b/apps/web/app/(with-contexts)/dashboard/page/[id]/page.tsx index 3572d18a6..5e03ba6d1 100644 --- a/apps/web/app/(with-contexts)/dashboard/page/[id]/page.tsx +++ b/apps/web/app/(with-contexts)/dashboard/page/[id]/page.tsx @@ -6,6 +6,7 @@ import { ProfileContext, ServerConfigContext, SiteInfoContext, + ThemeContext, TypefacesContext, } from "@components/contexts"; import { Profile } from "@courselit/common-models"; @@ -21,6 +22,7 @@ export default function Page({ params }: { params: { id: string } }) { const typefaces = useContext(TypefacesContext); const { profile } = useContext(ProfileContext); const config = useContext(ServerConfigContext); + const { theme } = useContext(ThemeContext); return ( { @@ -93,13 +96,22 @@ function LayoutContent({ }} > - - - - {children} - - - + + + + + {children} + + + + @@ -110,9 +122,10 @@ export default function Layout(props: { address: string; children: ReactNode; siteinfo: SiteInfo; - typefaces: Typeface[]; + theme: Theme; config: ServerConfig; session: Session | null; + // profile: Partial | null; }) { return ( @@ -120,3 +133,7 @@ export default function Layout(props: { ); } + +// function formatHSL(hsl: HSL): string { +// return `${hsl[0]} ${hsl[1]}% ${hsl[2]}%`; +// } diff --git a/apps/web/app/(with-contexts)/layout.tsx b/apps/web/app/(with-contexts)/layout.tsx index 6bf3e74a8..0affcbba3 100644 --- a/apps/web/app/(with-contexts)/layout.tsx +++ b/apps/web/app/(with-contexts)/layout.tsx @@ -1,9 +1,8 @@ import LayoutWithContext from "./layout-with-context"; import React from "react"; import { auth } from "@/auth"; -import { FetchBuilder } from "@courselit/utils"; import { headers } from "next/headers"; -import { getBackendAddress } from "@ui-lib/utils"; +import { getAddressFromHeaders, getFullSiteSetup } from "@ui-lib/utils"; import { defaultState } from "@components/default-state"; import { decode } from "base-64"; import { ServerConfig, SiteInfo } from "@courselit/common-models"; @@ -13,56 +12,10 @@ export default async function Layout({ }: { children: React.ReactNode; }) { - const headersList = headers(); - const address = getBackendAddress({ - "x-forwarded-proto": headersList.get("x-forwarded-proto"), - host: headersList.get("host"), - }); + const address = getAddressFromHeaders(headers); const session = await auth(); - const siteInfoQuery = ` - { site: getSiteInfo { - name, - settings { - title, - subtitle, - logo { - file, - caption - }, - currencyISOCode, - paymentMethod, - stripeKey, - codeInjectionHead, - codeInjectionBody, - mailingAddress, - hideCourseLitBranding, - razorpayKey, - lemonsqueezyStoreId, - lemonsqueezyOneTimeVariantId, - lemonsqueezySubscriptionMonthlyVariantId, - lemonsqueezySubscriptionYearlyVariantId, - }, - theme { - name, - active, - styles, - url - }, - typefaces { - section, - typeface, - fontWeights - }, - } - } - `; - const siteInfoFetch = new FetchBuilder() - .setUrl(`${address}/api/graph`) - .setPayload(siteInfoQuery) - .setIsGraphQLEndpoint(true) - .build(); - const siteInfoResponse = await siteInfoFetch.exec(); + const siteSetup = await getFullSiteSetup(address); const config: ServerConfig = { turnstileSiteKey: process.env.TURNSTILE_SITE_KEY || "", queueServer: process.env.QUEUE_SERVER || "", @@ -71,8 +24,8 @@ export default async function Layout({ return ( diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 061fafb4e..4ba9be1da 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,73 +1,47 @@ import "remirror/styles/all.css"; -import "@courselit/common-widgets/styles.css"; +import "@courselit/page-blocks/styles.css"; import "@courselit/components-library/styles.css"; +import "@courselit/page-primitives/styles.css"; import "../styles/globals.css"; import type { Metadata } from "next"; import { headers } from "next/headers"; -import { getBackendAddress } from "@ui-lib/utils"; -import { FetchBuilder } from "@courselit/utils"; -import { SiteInfo } from "@courselit/common-models"; +import { + getAddressFromHeaders, + getSiteInfo, + getFullSiteSetup, +} from "@ui-lib/utils"; +import * as fonts from "@/lib/fonts"; +import { generateThemeStyles } from "@/lib/theme-styles"; +import { Inter } from "next/font/google"; import { SITE_SETTINGS_DEFAULT_TITLE } from "@ui-config/strings"; +const inter = Inter({ subsets: ["latin"] }); + export async function generateMetadata(): Promise { - const headersList = headers(); - const address = getBackendAddress({ - "x-forwarded-proto": headersList.get("x-forwarded-proto"), - host: headersList.get("host"), - }); - const siteInfoQuery = ` - { site: getSiteInfo { - name, - settings { - title, - subtitle, - logo { - file, - caption - }, - currencyISOCode, - paymentMethod, - stripeKey, - codeInjectionHead, - codeInjectionBody, - mailingAddress, - hideCourseLitBranding, - razorpayKey, - lemonsqueezyStoreId, - lemonsqueezyOneTimeVariantId, - lemonsqueezySubscriptionMonthlyVariantId, - lemonsqueezySubscriptionYearlyVariantId, - }, - theme { - name, - active, - styles, - url - }, - typefaces { - section, - typeface, - fontWeights - }, - } - } - `; - const siteInfoFetch = new FetchBuilder() - .setUrl(`${address}/api/graph`) - .setPayload(siteInfoQuery) - .setIsGraphQLEndpoint(true) - .build(); - const siteInfoResponse = await siteInfoFetch.exec(); - let siteInfo: SiteInfo = {}; - if (siteInfoResponse.site.settings) { - siteInfo = siteInfoResponse.site.settings; - } + const address = getAddressFromHeaders(headers); + const siteInfo = await getSiteInfo(address); + return { - title: `${siteInfo.title || SITE_SETTINGS_DEFAULT_TITLE}`, + title: `${siteInfo?.title || SITE_SETTINGS_DEFAULT_TITLE}`, + description: siteInfo?.subtitle || "", openGraph: { + title: `${siteInfo?.title || SITE_SETTINGS_DEFAULT_TITLE}`, + description: siteInfo?.subtitle || "", + images: [ + { + url: siteInfo?.logo?.file as any, + alt: siteInfo?.logo?.caption || "", + }, + ], + }, + twitter: { + title: `${siteInfo?.title || SITE_SETTINGS_DEFAULT_TITLE}`, + description: siteInfo?.subtitle || "", images: [ - siteInfo.logo?.file as any, - "/courselit_backdrop_square.webp", + { + url: siteInfo?.logo?.file as any, + alt: siteInfo?.logo?.caption || "", + }, ], }, generator: "CourseLit", @@ -75,14 +49,27 @@ export async function generateMetadata(): Promise { }; } -export default function RootLayout({ - children, -}: { +interface RootLayoutProps { children: React.ReactNode; -}) { +} + +export default async function RootLayout({ children }: RootLayoutProps) { + const address = getAddressFromHeaders(headers); + const siteSetup = await getFullSiteSetup(address); + const themeStyles = siteSetup?.theme + ? generateThemeStyles(siteSetup.theme) + : ""; + return ( - {children} + + + + + {children} + ); } diff --git a/apps/web/app/verify-domain/route.ts b/apps/web/app/verify-domain/route.ts index 09e6b5130..ea35054d1 100644 --- a/apps/web/app/verify-domain/route.ts +++ b/apps/web/app/verify-domain/route.ts @@ -7,7 +7,7 @@ import { headers } from "next/headers"; import connectToDatabase from "../../services/db"; import { warn } from "@/services/logger"; -const { domainNameForSingleTenancy } = constants; +const { domainNameForSingleTenancy, schoolNameForSingleTenancy } = constants; const getDomainBasedOnSubdomain = async ( subdomain: string, @@ -142,6 +142,9 @@ export async function GET(req: Request) { lastMonthlyCountUpdate: new Date(), }, }, + settings: { + title: schoolNameForSingleTenancy, + }, }, { upsert: true, diff --git a/apps/web/components/admin/page-editor/add-widget.tsx b/apps/web/components/admin/page-editor/add-widget.tsx index 0c85ed3f3..92e709288 100644 --- a/apps/web/components/admin/page-editor/add-widget.tsx +++ b/apps/web/components/admin/page-editor/add-widget.tsx @@ -5,9 +5,6 @@ import { PageTypeBlog, PageTypeCommunity, } from "@courselit/common-models"; -import { IconButton } from "@courselit/components-library"; -import { Cross as Close } from "@courselit/icons"; -import { EDIT_PAGE_ADD_WIDGET_TITLE } from "../../../ui-config/strings"; import widgets from "../../../ui-config/widgets"; interface WidgetsListProps { @@ -19,14 +16,6 @@ interface WidgetsListProps { function AddWidget({ pageType, onSelection, onClose }: WidgetsListProps) { return (
    -
  • -

    - {EDIT_PAGE_ADD_WIDGET_TITLE} -

    - - - -
  • {Object.keys(widgets) .filter((widget) => !["header", "footer"].includes(widget)) .map((item, index) => diff --git a/apps/web/components/admin/page-editor/edit-widget.tsx b/apps/web/components/admin/page-editor/edit-widget.tsx index e5ed0da31..464468ed6 100644 --- a/apps/web/components/admin/page-editor/edit-widget.tsx +++ b/apps/web/components/admin/page-editor/edit-widget.tsx @@ -1,9 +1,8 @@ import { useState } from "react"; import widgets from "../../../ui-config/widgets"; import { AppDispatch, AppState } from "@courselit/state-management"; -import { Cross as Close } from "@courselit/icons"; +import { Button } from "@courselit/components-library"; import AdminWidget from "./admin-widget"; -import { IconButton, Button } from "@courselit/components-library"; interface EditWidgetProps { onChange: (widgetId: string, settings: Record) => void; @@ -31,30 +30,26 @@ export default function EditWidget({ }: EditWidgetProps) { const [deleteConfirmation, setDeleteConfirmation] = useState(false); const [hideActionButtons, setHideActionButtons] = useState(false); - const actualWidget = widgets[widget.name]; const [preservedStateAcrossRerender, setPreservedStateAcrossRerender] = useState>({}); + const actualWidget = widgets[widget.name]; + const onDeleteWidget = () => { + if (!widget.deleteable) { + return; + } + if (deleteConfirmation) { onDelete(widget.widgetId); - onClose(); } else { setDeleteConfirmation(true); + setTimeout(() => setDeleteConfirmation(false), 2000); } }; return ( -
    -
  • -

    - {actualWidget && actualWidget.metadata.displayName} - {!actualWidget && widget.name} -

    - - - -
  • +
    {actualWidget && (
    void; - onClose: () => void; -} - -function FontsList({ - draftTypefaces, - saveDraftTypefaces, - onClose, -}: FontListProps) { - const fonts = [ - "Roboto", - "Open Sans", - "Montserrat", - "Lato", - "Poppins", - "Source Sans Pro", - "Raleway", - "Noto Sans", - "Inter", - "Merriweather", - "Alegreya", - "Aleo", - "Muli", - "Arapey", - "Nunito", - "Carme", - "Rubik", - "Enriqueta", - ]; - const defaultTypeface = draftTypefaces.filter( - (x) => x.section === "default", - )[0]?.typeface; - - return ( -
      -
    • -

      - {EDIT_PAGE_BUTTON_FONTS} -

      - - - -
    • - {fonts.map((font) => ( -
    • saveDraftTypefaces(font)} - key={font} - > - {font} - {defaultTypeface === font && } -
    • - ))} -
    - ); -} - -export default FontsList; diff --git a/apps/web/components/admin/page-editor/fonts-list.tsx.notused b/apps/web/components/admin/page-editor/fonts-list.tsx.notused new file mode 100644 index 000000000..71994c7d8 --- /dev/null +++ b/apps/web/components/admin/page-editor/fonts-list.tsx.notused @@ -0,0 +1,58 @@ +import React from "react"; +import { Typeface } from "@courselit/common-models"; +import { Star } from "@courselit/icons"; + +interface FontListProps { + draftTypefaces: Typeface[]; + saveDraftTypefaces: (name: string) => void; + onClose: () => void; +} + +function FontsList({ + draftTypefaces, + saveDraftTypefaces, + onClose, +}: FontListProps) { + const fonts = [ + "Roboto", + "Open Sans", + "Montserrat", + "Lato", + "Poppins", + "Source Sans Pro", + "Raleway", + "Noto Sans", + "Inter", + "Merriweather", + "Alegreya", + "Aleo", + "Muli", + "Arapey", + "Nunito", + "Carme", + "Rubik", + "Enriqueta", + ]; + const defaultTypeface = draftTypefaces.filter( + (x) => x.section === "default", + )[0]?.typeface; + + return ( +
    +
      + {fonts.map((font) => ( +
    • saveDraftTypefaces(font)} + key={font} + > + {font} + {defaultTypeface === font && } +
    • + ))} +
    +
    + ); +} + +export default FontsList; diff --git a/apps/web/components/admin/page-editor/index.tsx b/apps/web/components/admin/page-editor/index.tsx index 4b2a95f16..ba9e47f1a 100644 --- a/apps/web/components/admin/page-editor/index.tsx +++ b/apps/web/components/admin/page-editor/index.tsx @@ -8,20 +8,16 @@ import { import type { Address, Media, Profile } from "@courselit/common-models"; import type { AppDispatch, AppState } from "@courselit/state-management"; import { networkAction } from "@courselit/state-management/dist/action-creators"; -import { - debounce, - FetchBuilder, - generateUniqueId, - getGraphQLQueryStringFromObject, -} from "@courselit/utils"; +import { debounce, FetchBuilder, generateUniqueId } from "@courselit/utils"; import { EDIT_PAGE_BUTTON_DONE, EDIT_PAGE_BUTTON_UPDATE, PAGE_TITLE_EDIT_PAGE, - EDIT_PAGE_BUTTON_FONTS, EDIT_PAGE_BUTTON_SEO, TOAST_TITLE_ERROR, EDIT_PAGE_BUTTON_VIEW, + EDIT_PAGE_BUTTON_THEME, + EDIT_PAGE_ADD_WIDGET_TITLE, } from "../../../ui-config/strings"; import { useRouter } from "next/navigation"; import { @@ -34,13 +30,36 @@ import dynamic from "next/dynamic"; import Head from "next/head"; import widgets from "../../../ui-config/widgets"; import { Sync, CheckCircled } from "@courselit/icons"; -import { Button, Skeleton, useToast } from "@courselit/components-library"; +import { Button2, Skeleton, useToast } from "@courselit/components-library"; import SeoEditor from "./seo-editor"; -import { ArrowUpFromLine, Eye, LogOut } from "lucide-react"; +import { ArrowUpFromLine, Eye, LogOut, Palette, Earth } from "lucide-react"; +import { cn } from "@/lib/shadcn-utils"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Separator } from "@/components/ui/separator"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import Link from "next/link"; +import { PanelHeader } from "./panel-header"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { ThemeWithDraftState } from "./theme-editor/theme-with-draft-state"; +import useThemes from "./use-themes"; +import NextThemeSwitcher from "./next-theme-switcher"; +import { useTheme } from "next-themes"; const EditWidget = dynamic(() => import("./edit-widget")); const AddWidget = dynamic(() => import("./add-widget")); -const FontsList = dynamic(() => import("./fonts-list")); +// const FontsList = dynamic(() => import("./fonts-list")); +const ThemeEditor = dynamic(() => import("./theme-editor/index")); const DEBOUNCE_TIME = 500; @@ -57,7 +76,7 @@ interface PageEditorProps { type LeftPaneContent = | "fonts" - | "themes" + | "theme" | "editor" | "widgets" | "seo" @@ -90,7 +109,14 @@ export default function PageEditor({ const [primaryFontFamily, setPrimaryFontFamily] = useState("Roboto, sans-serif"); const [loading, setLoading] = useState(false); + const [draftTheme, setDraftTheme] = useState( + state.theme, + ); const { toast } = useToast(); + const [pages, setPages] = useState([]); + const [loadingPages, setLoadingPages] = useState(true); + const { theme: lastEditedTheme } = useThemes(); + const { theme: nextTheme } = useTheme(); const router = useRouter(); const debouncedSave = useCallback( @@ -107,7 +133,44 @@ export default function PageEditor({ useEffect(() => { loadDraftTypefaces(); loadPage(); - }, []); + + loadPages(); + }, [address.backend, dispatch]); + + async function loadPages() { + setLoadingPages(true); + const query = ` + query { + pages: getPages(type: SITE) { + pageId, + name, + entityId, + deleteable + } + } + `; + const fetch = new FetchBuilder() + .setUrl(`${address.backend}/api/graph`) + .setPayload(query) + .setIsGraphQLEndpoint(true) + .build(); + try { + dispatch && dispatch(networkAction(true)); + const response = await fetch.exec(); + if (response.pages) { + setPages(response.pages); + } + } catch (err: any) { + toast({ + title: TOAST_TITLE_ERROR, + description: err.message || "Failed to load pages", + variant: "destructive", + }); + } finally { + setLoadingPages(false); + dispatch && dispatch(networkAction(false)); + } + } useEffect(() => { if (JSON.stringify(layout) !== JSON.stringify(page.draftLayout)) { @@ -124,6 +187,12 @@ export default function PageEditor({ } }, [draftTypefaces]); + useEffect(() => { + if (lastEditedTheme) { + setDraftTheme(lastEditedTheme); + } + }, [lastEditedTheme]); + const onItemClick = (widgetId: string) => { setLayout([...layout]); setSelectedWidget(widgetId); @@ -410,6 +479,7 @@ export default function PageEditor({ ); layout.splice(widgetIndex, 1); setLayout(layout); + onClose(); await savePage({ pageId: page.pageId!, layout }); }; @@ -434,74 +504,73 @@ export default function PageEditor({ }; const onClose = () => { - setSelectedWidget(); + setSelectedWidget(undefined); setLeftPaneContent("none"); }; const editWidget = useMemo( - () => ( - x.widgetId === selectedWidget)[0] - } - pageData={page.pageData || {}} - onChange={onWidgetSettingsChanged} - onClose={onClose} - onDelete={deleteWidget} - state={state as AppState} - dispatch={dispatch || (() => {})} - key={selectedWidget} - /> - ), + () => + page && + layout?.find((x) => x.widgetId === selectedWidget) && ( + x.widgetId === selectedWidget)} + pageData={page.pageData || {}} + onChange={onWidgetSettingsChanged} + onClose={onClose} + onDelete={deleteWidget} + state={state as AppState} + dispatch={dispatch || (() => {})} + key={selectedWidget} + /> + ), [selectedWidget], ); - const saveDraftTypefaces = async (fontName: string) => { - const newTypefaces: Typeface[] = structuredClone(draftTypefaces); - const defaultSection = newTypefaces.filter( - (x) => x.section === "default", - )[0]; - defaultSection.typeface = fontName; + // const saveDraftTypefaces = async (fontName: string) => { + // const newTypefaces: Typeface[] = structuredClone(draftTypefaces); + // const defaultSection = newTypefaces.filter( + // (x) => x.section === "default", + // )[0]; + // defaultSection.typeface = fontName; - const query = ` - mutation { - site: updateDraftTypefaces( - typefaces: ${getGraphQLQueryStringFromObject(newTypefaces)} - ) { - draftTypefaces { - section, - typeface, - fontWeights, - fontSize, - lineHeight, - letterSpacing, - case - }, - } - } - `; - const fetch = new FetchBuilder() - .setUrl(`${address.backend}/api/graph`) - .setPayload(query) - .setIsGraphQLEndpoint(true) - .build(); - try { - dispatch && dispatch(networkAction(true)); - const response = await fetch.exec(); - if (response.site) { - setDraftTypefaces(response.site.draftTypefaces); - } - } catch (err: any) { - toast({ - title: TOAST_TITLE_ERROR, - description: err.message, - variant: "destructive", - }); - } finally { - dispatch && dispatch(networkAction(false)); - } - }; + // const query = ` + // mutation { + // site: updateDraftTypefaces( + // typefaces: ${getGraphQLQueryStringFromObject(newTypefaces)} + // ) { + // draftTypefaces { + // section, + // typeface, + // fontWeights, + // fontSize, + // lineHeight, + // letterSpacing, + // case + // }, + // } + // } + // `; + // const fetch = new FetchBuilder() + // .setUrl(`${address.backend}/api/graph`) + // .setPayload(query) + // .setIsGraphQLEndpoint(true) + // .build(); + // try { + // dispatch && dispatch(networkAction(true)); + // const response = await fetch.exec(); + // if (response.site) { + // setDraftTypefaces(response.site.draftTypefaces); + // } + // } catch (err: any) { + // toast({ + // title: TOAST_TITLE_ERROR, + // description: err.message, + // variant: "destructive", + // }); + // } finally { + // dispatch && dispatch(networkAction(false)); + // } + // }; const onAddWidgetBelow = (index: number) => { setSelectedWidgetIndex(index); @@ -524,12 +593,20 @@ export default function PageEditor({ /> )} {leftPaneContent === "editor" && editWidget} - {leftPaneContent === "fonts" && ( + {/* {leftPaneContent === "fonts" && ( + )} */} + {leftPaneContent === "theme" && ( + { + setDraftTheme(theme); + }} + colorMode={nextTheme === "dark" ? "dark" : "light"} + /> )} {leftPaneContent === "seo" && ( 0) { return ( -
    +
    - {`${PAGE_TITLE_EDIT_PAGE} ${ - page.name || "" - }`} + {`${PAGE_TITLE_EDIT_PAGE} ${page.name || ""}`} {fontString && } -
    -
    -
    - - +
    +
    +
    +
    + {loadingPages ? ( +
    + +
    + ) : pages.length > 0 && page.pageId ? ( + + ) : ( +
    + No pages found +
    + )} +
    -
    - {loading && } - {!loading && } - - - + + + + + + + + + + + +

    {EDIT_PAGE_BUTTON_VIEW}

    +
    +
    +
    + + + + + + + + + + +

    {EDIT_PAGE_BUTTON_DONE}

    +
    +
    +
    +
    -
    +
    {leftPaneContent !== "none" && ( -
    - {activeSidePaneContent} +
    + + +
    + {activeSidePaneContent} +
    +
    )} -
    - {draftTypefaces.length === 0 && ( -
    - -
    - )} - {draftTypefaces.length > 0 && ( -