diff --git a/.github/workflows/publish-course-app.yml b/.github/workflows/publish-course-app.yml new file mode 100644 index 00000000..bbacc767 --- /dev/null +++ b/.github/workflows/publish-course-app.yml @@ -0,0 +1,26 @@ +name: Publish to NPM +on: + release: + types: [created] +jobs: + build: + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: 'https://registry.npmjs.org' + + - uses: pnpm/action-setup@v4 + + - name: Install dependencies & publish + working-directory: packages/course-app + run: | + pnpm i + pnpm publish --no-git-checks + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/.nvmrc b/.nvmrc index 8ddbc0c6..7950a445 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18.16.0 +v18.17.0 diff --git a/.prettierrc b/.prettierrc index 73195ab7..16b3fd86 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,11 +3,12 @@ "singleQuote": true, "trailingComma": "none", "printWidth": 100, + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], "overrides": [ { - "files": ["*.svelte"], + "files": "*.svelte", "options": { - "svelteBracketNewLine": true + "parser": "svelte" } } ] diff --git a/apps/classroomio-com/package.json b/apps/classroomio-com/package.json index 1e34f058..6a23eabd 100644 --- a/apps/classroomio-com/package.json +++ b/apps/classroomio-com/package.json @@ -26,6 +26,9 @@ "vite": "^5.0.0" }, "type": "module", + "engines": { + "node": "^18.17.0" + }, "dependencies": { "html-to-image": "^1.11.11", "just-extend": "^6.2.0", diff --git a/apps/classroomio-com/static/hero-bg.svg b/apps/classroomio-com/static/hero-bg.svg deleted file mode 100644 index 4fa4c617..00000000 --- a/apps/classroomio-com/static/hero-bg.svg +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/apps/classroomio-com/static/tools-hero-bg.svg b/apps/classroomio-com/static/tools-hero-bg.svg deleted file mode 100644 index 6d9cd38e..00000000 --- a/apps/classroomio-com/static/tools-hero-bg.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/apps/course-app/.gitignore b/apps/course-app/.gitignore new file mode 100644 index 00000000..3b462cb0 --- /dev/null +++ b/apps/course-app/.gitignore @@ -0,0 +1,23 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/apps/course-app/.npmrc b/apps/course-app/.npmrc new file mode 100644 index 00000000..b6f27f13 --- /dev/null +++ b/apps/course-app/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/apps/course-app/.prettierignore b/apps/course-app/.prettierignore new file mode 100644 index 00000000..ab78a95d --- /dev/null +++ b/apps/course-app/.prettierignore @@ -0,0 +1,4 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock diff --git a/apps/course-app/.prettierrc b/apps/course-app/.prettierrc new file mode 100644 index 00000000..abaa84f9 --- /dev/null +++ b/apps/course-app/.prettierrc @@ -0,0 +1,15 @@ +{ + "useTabs": false, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} diff --git a/apps/course-app/README.md b/apps/course-app/README.md new file mode 100644 index 00000000..d97d5fa1 --- /dev/null +++ b/apps/course-app/README.md @@ -0,0 +1,17 @@ +# Course App + +Create a course site for your Product. With our prebuilt templates, you can create a fully functional course site in minutes. + +## Tech Stack + +- SvelteKit +- TailwindCSS +- Mdx - Courses are written in markdown + +## How to use the package + +```bash +pnpm dlx @cio/course-app mysaas-university +``` + +This will create a folder called `mysaas-university` with a fully functional course site. diff --git a/apps/course-app/components.json b/apps/course-app/components.json new file mode 100644 index 00000000..b2c229a7 --- /dev/null +++ b/apps/course-app/components.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://shadcn-svelte.com/schema.json", + "style": "new-york", + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/app.css", + "baseColor": "zinc" + }, + "aliases": { + "components": "$lib/components", + "utils": "$lib/utils" + }, + "typescript": true +} \ No newline at end of file diff --git a/apps/course-app/package.json b/apps/course-app/package.json new file mode 100644 index 00000000..bc39628e --- /dev/null +++ b/apps/course-app/package.json @@ -0,0 +1,46 @@ +{ + "name": "course-app", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "format": "prettier --write .", + "lint": "prettier --check .", + "test:unit": "vitest", + "test": "npm run test:unit -- --run" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/kit": "^2.9.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "autoprefixer": "^10.4.20", + "bits-ui": "^0.21.16", + "clsx": "^2.1.1", + "mode-watcher": "^0.5.0", + "prettier": "^3.3.2", + "prettier-plugin-svelte": "^3.2.6", + "prettier-plugin-tailwindcss": "^0.6.5", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "svelte-sonner": "^0.3.28", + "tailwind-merge": "^2.5.5", + "tailwind-variants": "^0.3.0", + "tailwindcss": "^3.4.9", + "typescript": "^5.0.0", + "vite": "^6.0.0", + "vitest": "^2.0.4" + }, + "dependencies": { + "@tailwindcss/container-queries": "^0.1.1", + "@tailwindcss/forms": "^0.5.9", + "@tailwindcss/typography": "^0.5.15", + "lucide-svelte": "^0.468.0", + "svelte-inview": "^4.0.4", + "svelte-motion": "^0.12.2" + } +} diff --git a/apps/course-app/postcss.config.js b/apps/course-app/postcss.config.js new file mode 100644 index 00000000..0f772168 --- /dev/null +++ b/apps/course-app/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {} + } +}; diff --git a/apps/course-app/src/app.css b/apps/course-app/src/app.css new file mode 100644 index 00000000..6662813b --- /dev/null +++ b/apps/course-app/src/app.css @@ -0,0 +1,78 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; + + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; + + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + + --destructive: 0 72.2% 50.6%; + --destructive-foreground: 0 0% 98%; + + --ring: 240 10% 3.9%; + + --radius: 0.5rem; + } + + .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + + --ring: 240 4.9% 83.9%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/apps/course-app/src/app.d.ts b/apps/course-app/src/app.d.ts new file mode 100644 index 00000000..da08e6da --- /dev/null +++ b/apps/course-app/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/apps/course-app/src/app.html b/apps/course-app/src/app.html new file mode 100644 index 00000000..d91e6d06 --- /dev/null +++ b/apps/course-app/src/app.html @@ -0,0 +1,45 @@ + + + + + + + + + Courseapp by ClassroomIO – Create your SAAS Academy in Minutes + + + + + + + + + + + + + + + + + + + + + + + + + + %sveltekit.head% + + + +
%sveltekit.body%
+ + + \ No newline at end of file diff --git a/apps/course-app/src/demo.spec.ts b/apps/course-app/src/demo.spec.ts new file mode 100644 index 00000000..e07cbbd7 --- /dev/null +++ b/apps/course-app/src/demo.spec.ts @@ -0,0 +1,7 @@ +import { describe, it, expect } from 'vitest'; + +describe('sum test', () => { + it('adds 1 + 2 to equal 3', () => { + expect(1 + 2).toBe(3); + }); +}); diff --git a/apps/course-app/src/lib/components/animation/blurfade/blurfade.svelte b/apps/course-app/src/lib/components/animation/blurfade/blurfade.svelte new file mode 100644 index 00000000..1c87e4b8 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/blurfade/blurfade.svelte @@ -0,0 +1,48 @@ + + + + +
{ + isInView = detail.inView ? 'visible' : 'hidden'; + }} + class={cn(className)} + > + Default +
+
+
diff --git a/apps/course-app/src/lib/components/animation/blurfade/index.ts b/apps/course-app/src/lib/components/animation/blurfade/index.ts new file mode 100644 index 00000000..3b094189 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/blurfade/index.ts @@ -0,0 +1 @@ +export { default as BlurFade } from './blurfade.svelte'; diff --git a/apps/course-app/src/lib/components/animation/blurin/blurin.svelte b/apps/course-app/src/lib/components/animation/blurin/blurin.svelte new file mode 100644 index 00000000..8deec74d --- /dev/null +++ b/apps/course-app/src/lib/components/animation/blurin/blurin.svelte @@ -0,0 +1,36 @@ + + + +

+ +

+
diff --git a/apps/course-app/src/lib/components/animation/blurin/index.ts b/apps/course-app/src/lib/components/animation/blurin/index.ts new file mode 100644 index 00000000..cbe173d9 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/blurin/index.ts @@ -0,0 +1 @@ +export { default as BlurIn } from './blurin.svelte'; diff --git a/apps/course-app/src/lib/components/animation/card/body.svelte b/apps/course-app/src/lib/components/animation/card/body.svelte new file mode 100644 index 00000000..2b29d82d --- /dev/null +++ b/apps/course-app/src/lib/components/animation/card/body.svelte @@ -0,0 +1,21 @@ + + +
+ + + {@render children()} +
diff --git a/apps/course-app/src/lib/components/animation/card/container.svelte b/apps/course-app/src/lib/components/animation/card/container.svelte new file mode 100644 index 00000000..48b492ba --- /dev/null +++ b/apps/course-app/src/lib/components/animation/card/container.svelte @@ -0,0 +1,30 @@ + + +
+
+
+
+ +
+ Default +
+
+
diff --git a/apps/course-app/src/lib/components/animation/card/ellipses.svelte b/apps/course-app/src/lib/components/animation/card/ellipses.svelte new file mode 100644 index 00000000..ce0bc9d2 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/card/ellipses.svelte @@ -0,0 +1,13 @@ + + +
+
+
+
+
+
+
+
diff --git a/apps/course-app/src/lib/components/animation/card/index.ts b/apps/course-app/src/lib/components/animation/card/index.ts new file mode 100644 index 00000000..9295f8dd --- /dev/null +++ b/apps/course-app/src/lib/components/animation/card/index.ts @@ -0,0 +1,2 @@ +export { default as CardContainer } from './container.svelte'; +export { default as CardBody } from './body.svelte'; diff --git a/apps/course-app/src/lib/components/animation/gradual-spacing/gradual-spacing.svelte b/apps/course-app/src/lib/components/animation/gradual-spacing/gradual-spacing.svelte new file mode 100644 index 00000000..47666628 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/gradual-spacing/gradual-spacing.svelte @@ -0,0 +1,42 @@ + + +
+ + {#each wordsspilit as char, i} + + + {#if char === ' '} +   + {:else} + {char} + {/if} + + + {/each} + +
diff --git a/apps/course-app/src/lib/components/animation/gradual-spacing/index.ts b/apps/course-app/src/lib/components/animation/gradual-spacing/index.ts new file mode 100644 index 00000000..8f068a0e --- /dev/null +++ b/apps/course-app/src/lib/components/animation/gradual-spacing/index.ts @@ -0,0 +1 @@ +export { default as GradualSpacing } from './gradual-spacing.svelte'; diff --git a/apps/course-app/src/lib/components/animation/magic-card/index.ts b/apps/course-app/src/lib/components/animation/magic-card/index.ts new file mode 100644 index 00000000..949fc79c --- /dev/null +++ b/apps/course-app/src/lib/components/animation/magic-card/index.ts @@ -0,0 +1 @@ +export { default as MagicCard } from './magic-card.svelte'; diff --git a/apps/course-app/src/lib/components/animation/magic-card/magic-card.svelte b/apps/course-app/src/lib/components/animation/magic-card/magic-card.svelte new file mode 100644 index 00000000..57ab3456 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/magic-card/magic-card.svelte @@ -0,0 +1,72 @@ + + + + +
+
+ + +
+

Magic Card

+
+
+
+ +
+ +
+ + diff --git a/apps/course-app/src/lib/components/animation/meteors/index.ts b/apps/course-app/src/lib/components/animation/meteors/index.ts new file mode 100644 index 00000000..dfe2fd78 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/meteors/index.ts @@ -0,0 +1 @@ +export { default as Meteors } from './meteors.svelte'; diff --git a/apps/course-app/src/lib/components/animation/meteors/meteors.svelte b/apps/course-app/src/lib/components/animation/meteors/meteors.svelte new file mode 100644 index 00000000..8257629d --- /dev/null +++ b/apps/course-app/src/lib/components/animation/meteors/meteors.svelte @@ -0,0 +1,36 @@ + + +{#each meteorStyles as style, idx} + + +
+ +{/each} diff --git a/apps/course-app/src/lib/components/animation/shimmerbutton/index.ts b/apps/course-app/src/lib/components/animation/shimmerbutton/index.ts new file mode 100644 index 00000000..cefa8f5f --- /dev/null +++ b/apps/course-app/src/lib/components/animation/shimmerbutton/index.ts @@ -0,0 +1 @@ +export { default as ShimmerButton } from './shimmerbutton.svelte'; diff --git a/apps/course-app/src/lib/components/animation/shimmerbutton/shimmerbutton.svelte b/apps/course-app/src/lib/components/animation/shimmerbutton/shimmerbutton.svelte new file mode 100644 index 00000000..67fe6ba4 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/shimmerbutton/shimmerbutton.svelte @@ -0,0 +1,62 @@ + + + diff --git a/apps/course-app/src/lib/components/animation/shinny-text/index.ts b/apps/course-app/src/lib/components/animation/shinny-text/index.ts new file mode 100644 index 00000000..a969c4fb --- /dev/null +++ b/apps/course-app/src/lib/components/animation/shinny-text/index.ts @@ -0,0 +1 @@ +export { default as ShinnyText } from './shinny-text.svelte'; diff --git a/apps/course-app/src/lib/components/animation/shinny-text/shinny-text.svelte b/apps/course-app/src/lib/components/animation/shinny-text/shinny-text.svelte new file mode 100644 index 00000000..55cd431f --- /dev/null +++ b/apps/course-app/src/lib/components/animation/shinny-text/shinny-text.svelte @@ -0,0 +1,24 @@ + + +

+ Shimmer Animation +

diff --git a/apps/course-app/src/lib/components/animation/square-card/body.svelte b/apps/course-app/src/lib/components/animation/square-card/body.svelte new file mode 100644 index 00000000..2e4fa892 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/square-card/body.svelte @@ -0,0 +1,9 @@ + + +
+ {@render children()} +
diff --git a/apps/course-app/src/lib/components/animation/square-card/icon.svelte b/apps/course-app/src/lib/components/animation/square-card/icon.svelte new file mode 100644 index 00000000..c26a3cc5 --- /dev/null +++ b/apps/course-app/src/lib/components/animation/square-card/icon.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/apps/course-app/src/lib/components/animation/square-card/index.ts b/apps/course-app/src/lib/components/animation/square-card/index.ts new file mode 100644 index 00000000..e1b6b6db --- /dev/null +++ b/apps/course-app/src/lib/components/animation/square-card/index.ts @@ -0,0 +1,2 @@ +export { default as SquareCardBody } from './body.svelte'; +export { default as SquareCardIcon } from './icon.svelte'; diff --git a/apps/course-app/src/lib/components/navigation/index.ts b/apps/course-app/src/lib/components/navigation/index.ts new file mode 100644 index 00000000..3c5d3d38 --- /dev/null +++ b/apps/course-app/src/lib/components/navigation/index.ts @@ -0,0 +1 @@ +export { default as Navigation } from './navigation.svelte'; diff --git a/apps/course-app/src/lib/components/navigation/navigation.svelte b/apps/course-app/src/lib/components/navigation/navigation.svelte new file mode 100644 index 00000000..4206d9f9 --- /dev/null +++ b/apps/course-app/src/lib/components/navigation/navigation.svelte @@ -0,0 +1,106 @@ + + +
+

+ brought to you by + classroomio.com + + + +

+ +
diff --git a/apps/course-app/src/lib/components/template-card/index.ts b/apps/course-app/src/lib/components/template-card/index.ts new file mode 100644 index 00000000..93ba23f5 --- /dev/null +++ b/apps/course-app/src/lib/components/template-card/index.ts @@ -0,0 +1 @@ +export { default as TemplateCard } from './template-card.svelte'; diff --git a/apps/course-app/src/lib/components/template-card/template-card.svelte b/apps/course-app/src/lib/components/template-card/template-card.svelte new file mode 100644 index 00000000..3ac950f3 --- /dev/null +++ b/apps/course-app/src/lib/components/template-card/template-card.svelte @@ -0,0 +1,38 @@ + + +
+ +
+

{template.name}

+
+
diff --git a/apps/course-app/src/lib/components/ui/button/button.svelte b/apps/course-app/src/lib/components/ui/button/button.svelte new file mode 100644 index 00000000..86827f32 --- /dev/null +++ b/apps/course-app/src/lib/components/ui/button/button.svelte @@ -0,0 +1,25 @@ + + + + + diff --git a/apps/course-app/src/lib/components/ui/button/index.ts b/apps/course-app/src/lib/components/ui/button/index.ts new file mode 100644 index 00000000..f0f46124 --- /dev/null +++ b/apps/course-app/src/lib/components/ui/button/index.ts @@ -0,0 +1,50 @@ +import type { Button as ButtonPrimitive } from "bits-ui"; +import { type VariantProps, tv } from "tailwind-variants"; +import Root from "./button.svelte"; + +const buttonVariants = tv({ + base: "focus-visible:ring-ring inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50", + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90 shadow", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm", + outline: + "border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm", + secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, +}); + +type Variant = VariantProps["variant"]; +type Size = VariantProps["size"]; + +type Props = ButtonPrimitive.Props & { + variant?: Variant; + size?: Size; +}; + +type Events = ButtonPrimitive.Events; + +export { + Root, + type Props, + type Events, + // + Root as Button, + type Props as ButtonProps, + type Events as ButtonEvents, + buttonVariants, +}; diff --git a/apps/course-app/src/lib/components/ui/separator/index.ts b/apps/course-app/src/lib/components/ui/separator/index.ts new file mode 100644 index 00000000..82442d2c --- /dev/null +++ b/apps/course-app/src/lib/components/ui/separator/index.ts @@ -0,0 +1,7 @@ +import Root from "./separator.svelte"; + +export { + Root, + // + Root as Separator, +}; diff --git a/apps/course-app/src/lib/components/ui/separator/separator.svelte b/apps/course-app/src/lib/components/ui/separator/separator.svelte new file mode 100644 index 00000000..aa61f693 --- /dev/null +++ b/apps/course-app/src/lib/components/ui/separator/separator.svelte @@ -0,0 +1,22 @@ + + + diff --git a/apps/course-app/src/lib/components/ui/sonner/index.ts b/apps/course-app/src/lib/components/ui/sonner/index.ts new file mode 100644 index 00000000..1ad9f4a2 --- /dev/null +++ b/apps/course-app/src/lib/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from "./sonner.svelte"; diff --git a/apps/course-app/src/lib/components/ui/sonner/sonner.svelte b/apps/course-app/src/lib/components/ui/sonner/sonner.svelte new file mode 100644 index 00000000..7d5b2f14 --- /dev/null +++ b/apps/course-app/src/lib/components/ui/sonner/sonner.svelte @@ -0,0 +1,20 @@ + + + diff --git a/apps/course-app/src/lib/index.ts b/apps/course-app/src/lib/index.ts new file mode 100644 index 00000000..856f2b6c --- /dev/null +++ b/apps/course-app/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/apps/course-app/src/lib/utils.ts b/apps/course-app/src/lib/utils.ts new file mode 100644 index 00000000..88712453 --- /dev/null +++ b/apps/course-app/src/lib/utils.ts @@ -0,0 +1,62 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; +import { cubicOut } from "svelte/easing"; +import type { TransitionConfig } from "svelte/transition"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +type FlyAndScaleParams = { + y?: number; + x?: number; + start?: number; + duration?: number; +}; + +export const flyAndScale = ( + node: Element, + params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 } +): TransitionConfig => { + const style = getComputedStyle(node); + const transform = style.transform === "none" ? "" : style.transform; + + const scaleConversion = ( + valueA: number, + scaleA: [number, number], + scaleB: [number, number] + ) => { + const [minA, maxA] = scaleA; + const [minB, maxB] = scaleB; + + const percentage = (valueA - minA) / (maxA - minA); + const valueB = percentage * (maxB - minB) + minB; + + return valueB; + }; + + const styleToString = ( + style: Record + ): string => { + return Object.keys(style).reduce((str, key) => { + if (style[key] === undefined) return str; + return str + `${key}:${style[key]};`; + }, ""); + }; + + return { + duration: params.duration ?? 200, + delay: 0, + css: (t) => { + const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]); + const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]); + const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]); + + return styleToString({ + transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`, + opacity: t + }); + }, + easing: cubicOut + }; +}; \ No newline at end of file diff --git a/apps/course-app/src/routes/+layout.svelte b/apps/course-app/src/routes/+layout.svelte new file mode 100644 index 00000000..1b0be2c8 --- /dev/null +++ b/apps/course-app/src/routes/+layout.svelte @@ -0,0 +1,14 @@ + + + + + + +{@render children()} diff --git a/apps/course-app/src/routes/+page.svelte b/apps/course-app/src/routes/+page.svelte new file mode 100644 index 00000000..62f6b8c1 --- /dev/null +++ b/apps/course-app/src/routes/+page.svelte @@ -0,0 +1,185 @@ + + + +
+
+ + + + Create a UNIVERSITY + Site for your SAAS in + MINUTES + + + +

+ With our prebuilt templates, you can setup a fully functional customer educational site + for your product in minutes.
+ + Similar to the likes of + + + + + + +

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

+ Browser Templates +

+ +
+ {#each templates as template, index} + + + + {/each} +
+
+
+ + + + + + +
+ +
+ + +
+ + +

+ Start Customising Your University Site +

+ +
+ { + window.location.href = + 'https://github.com/classroomio/classroomio/blob/a6af412b2401a8dc3f871773b96a24b2fa764f52/packages/course-app/README.md'; + }} + class="shadow-2xl" + > + + Go to Docs + + +
+
+
+
+
+ + + + + + +
+
+

+ Made with ❤️ by classroomio +

+
+
+ diff --git a/apps/course-app/static/apple-touch-icon.png b/apps/course-app/static/apple-touch-icon.png new file mode 100644 index 00000000..bcb8778b Binary files /dev/null and b/apps/course-app/static/apple-touch-icon.png differ diff --git a/apps/course-app/static/favicon.ico b/apps/course-app/static/favicon.ico new file mode 100644 index 00000000..620b1a94 Binary files /dev/null and b/apps/course-app/static/favicon.ico differ diff --git a/apps/course-app/static/favicon.png b/apps/course-app/static/favicon.png new file mode 100644 index 00000000..825b9e65 Binary files /dev/null and b/apps/course-app/static/favicon.png differ diff --git a/apps/course-app/static/logo-16.png b/apps/course-app/static/logo-16.png new file mode 100644 index 00000000..a8340e33 Binary files /dev/null and b/apps/course-app/static/logo-16.png differ diff --git a/apps/course-app/static/logo-192.png b/apps/course-app/static/logo-192.png new file mode 100644 index 00000000..8675c2fa Binary files /dev/null and b/apps/course-app/static/logo-192.png differ diff --git a/apps/course-app/static/logo-32.png b/apps/course-app/static/logo-32.png new file mode 100644 index 00000000..4b2d7f2b Binary files /dev/null and b/apps/course-app/static/logo-32.png differ diff --git a/apps/course-app/static/logo-512.png b/apps/course-app/static/logo-512.png new file mode 100644 index 00000000..be3dec8e Binary files /dev/null and b/apps/course-app/static/logo-512.png differ diff --git a/apps/course-app/static/site.webmanifest b/apps/course-app/static/site.webmanifest new file mode 100644 index 00000000..45dc8a20 --- /dev/null +++ b/apps/course-app/static/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/apps/course-app/svelte.config.js b/apps/course-app/svelte.config.js new file mode 100644 index 00000000..e00ee639 --- /dev/null +++ b/apps/course-app/svelte.config.js @@ -0,0 +1,23 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://svelte.dev/docs/kit/integrations + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter(), + + alias: { + $lib: './src/lib', + $lib: './src/lib/*' + } + } +}; + +export default config; diff --git a/apps/course-app/tailwind.config.ts b/apps/course-app/tailwind.config.ts new file mode 100644 index 00000000..13d7f07c --- /dev/null +++ b/apps/course-app/tailwind.config.ts @@ -0,0 +1,111 @@ +import { fontFamily } from 'tailwindcss/defaultTheme'; +import type { Config } from 'tailwindcss'; + +const config: Config = { + darkMode: ['class'], + content: ['./src/**/*.{html,js,svelte,ts}'], + safelist: ['dark'], + theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px' + } + }, + extend: { + colors: { + border: 'hsl(var(--border) / )', + input: 'hsl(var(--input) / )', + ring: 'hsl(var(--ring) / )', + background: 'hsl(var(--background) / )', + foreground: 'hsl(var(--foreground) / )', + primary: { + DEFAULT: 'hsl(var(--primary) / )', + foreground: 'hsl(var(--primary-foreground) / )' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary) / )', + foreground: 'hsl(var(--secondary-foreground) / )' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive) / )', + foreground: 'hsl(var(--destructive-foreground) / )' + }, + muted: { + DEFAULT: 'hsl(var(--muted) / )', + foreground: 'hsl(var(--muted-foreground) / )' + }, + accent: { + DEFAULT: 'hsl(var(--accent) / )', + foreground: 'hsl(var(--accent-foreground) / )' + }, + popover: { + DEFAULT: 'hsl(var(--popover) / )', + foreground: 'hsl(var(--popover-foreground) / )' + }, + card: { + DEFAULT: 'hsl(var(--card) / )', + foreground: 'hsl(var(--card-foreground) / )' + } + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + fontFamily: { + sans: [...fontFamily.sans] + }, + + animation: { + meteor: 'meteor 5s linear infinite', + shimmer: 'shimmer 8s infinite', + 'spin-around': 'spin-around calc(var(--speed) * 2) infinite linear', + magicslide: 'magicslide var(--speed) ease-in-out infinite alternate' + }, + + keyframes: { + meteor: { + '0%': { transform: 'rotate(215deg) translateX(0)', opacity: '1' }, + '70%': { opacity: '1' }, + '100%': { + transform: 'rotate(215deg) translateX(-500px)', + opacity: '0' + } + }, + shimmer: { + '0%, 90%, 100%': { + 'background-position': 'calc(-100% - var(--shimmer-width)) 0' + }, + '30%, 60%': { + 'background-position': 'calc(100% + var(--shimmer-width)) 0' + } + }, + + 'spin-around': { + '0%': { + transform: 'translateZ(0) rotate(0)' + }, + '15%, 35%': { + transform: 'translateZ(0) rotate(90deg)' + }, + '65%, 85%': { + transform: 'translateZ(0) rotate(270deg)' + }, + '100%': { + transform: 'translateZ(0) rotate(360deg)' + } + }, + + magicslide: { + to: { + transform: 'translate(calc(100cqw - 100%), 0)' + } + } + } + } + } +}; + +export default config; diff --git a/apps/course-app/tsconfig.json b/apps/course-app/tsconfig.json new file mode 100644 index 00000000..f4d0a0e1 --- /dev/null +++ b/apps/course-app/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/apps/course-app/vite.config.ts b/apps/course-app/vite.config.ts new file mode 100644 index 00000000..c63c90e7 --- /dev/null +++ b/apps/course-app/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'vitest/config'; +import { sveltekit } from '@sveltejs/kit/vite'; +import path from 'path'; + +export default defineConfig({ + plugins: [sveltekit()], + + test: { + include: ['src/**/*.{test,spec}.{js,ts}'] + }, + + resolve: { + alias: { + $lib: path.resolve('./src/lib') + } + } +}); diff --git a/apps/dashboard/.nvmrc b/apps/dashboard/.nvmrc index 8ddbc0c6..7950a445 100644 --- a/apps/dashboard/.nvmrc +++ b/apps/dashboard/.nvmrc @@ -1 +1 @@ -v18.16.0 +v18.17.0 diff --git a/apps/dashboard/Dockerfile.prod b/apps/dashboard/Dockerfile.prod new file mode 100644 index 00000000..3d758d77 --- /dev/null +++ b/apps/dashboard/Dockerfile.prod @@ -0,0 +1,55 @@ +# FROM node:18-alpine + +# WORKDIR /app + +# RUN npm install -g pnpm + +# COPY . . + + + +# # Install dependencies and build +# RUN pnpm install --frozen-lockfile && \ +# cd apps/dashboard && \ +# pnpm build || { echo "Build failed! Error output above."; exit 1; } + +# EXPOSE 5004 + +# ENV NODE_ENV=production +# CMD [ "pnpm", "dashboard:start" ] + +FROM node:18-alpine AS builder +WORKDIR /app +RUN npm install -g pnpm +COPY package.json . +COPY pnpm-lock.yaml . +RUN pnpm install --frozen-lockfile +COPY . . + +# Set environment variables +ENV IS_SELFHOSTED=true +ENV DEPLOYMENT_PROVIDER=docker +ENV PUBLIC_SUPABASE_URL=$PUBLIC_SUPABASE_URL +ENV PUBLIC_SUPABASE_ANON_KEY=$PUBLIC_SUPABASE_ANON_KEY +ENV PRIVATE_SUPABASE_SERVICE_ROLE=$PRIVATE_SUPABASE_SERVICE_ROLE +ENV PRIVATE_APP_HOST=$PRIVATE_APP_HOST +ENV PRIVATE_APP_SUBDOMAINS=$PRIVATE_APP_SUBDOMAINS +ENV UNSPLASH_API_KEY=$UNSPLASH_API_KEY +ENV OPENAI_API_KEY=$OPENAI_API_KEY +ENV PUBLIC_SERVER_URL=$PUBLIC_SERVER_URL +ENV PUBLIC_IP_REGISTRY_KEY=$PUBLIC_IP_REGISTRY_KEY +ENV NODE_OPTIONS="--max-old-space-size=10240" + +WORKDIR /app/apps/dashboard +RUN pnpm install --frozen-lockfile +RUN IS_SELFHOSTED=true DEPLOYMENT_PROVIDER=docker pnpm build +RUN pnpm prune --production + +FROM node:18-alpine +WORKDIR /app +COPY --from=builder /app/apps/dashboard/build build/ +COPY --from=builder /app/apps/dashboard/node_modules node_modules/ +COPY package.json . +EXPOSE 3004 +ENV NODE_ENV=production +CMD [ "node", "build" ] diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 76f7a18a..5926c6d8 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -109,7 +109,7 @@ }, "type": "module", "engines": { - "node": "18.x" + "node": "^18.17.0" }, "browser": { "crypto": false diff --git a/package.json b/package.json index 142d174f..8ba145c1 100644 --- a/package.json +++ b/package.json @@ -27,5 +27,8 @@ "supabase": "^1.151.1", "tsconfig": "workspace:*", "typescript": "^5.1.6" + }, + "engines": { + "node": "^18.17.0" } } diff --git a/packages/course-app/.gitignore b/packages/course-app/.gitignore new file mode 100644 index 00000000..48c1730b --- /dev/null +++ b/packages/course-app/.gitignore @@ -0,0 +1,22 @@ +node_modules + +# Output +.output +.vercel +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db +dist + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/packages/course-app/README.md b/packages/course-app/README.md new file mode 100644 index 00000000..ff9212ae --- /dev/null +++ b/packages/course-app/README.md @@ -0,0 +1,27 @@ +# @classroomio/course-app + +Create a university site for your SAAS in seconds, ready to go. Just add your content. + +## Usage + +To create a template run: + +```bash +npx @cio/course-app@latest name-of-your-project -t name-of-template --course +``` + +### Commands + +The CLI accepts the following arguments + +- argument 1 : `name-of-project` this argument is the name of your project +- argument 2 : `--template name-of-template` this argument is the name of the template you wish to use +- argument 3 : `--courses` an option to use our demo courses + +## Available Templates + +- cal +- classic +- minimalistic +- posthog +- webflow diff --git a/packages/course-app/bin/create-template.ts b/packages/course-app/bin/create-template.ts new file mode 100644 index 00000000..9cbbe6c3 --- /dev/null +++ b/packages/course-app/bin/create-template.ts @@ -0,0 +1,84 @@ +#!/usr/bin/env node + +import { program } from 'commander'; +import chalk from 'chalk'; +import inquirer from 'inquirer'; +import ora from 'ora'; +import path from 'path'; +import TEMPLATES from '../templates.json'; +import { scaffoldTemplate } from '../lib/scaffoldTemplate'; + +const spinner = ora(); + +const templates = TEMPLATES.map((template) => template.name); + +program.version('0.0.1').description('Courseapp CLI'); + +program + .argument('[projectName]', 'name of the project') + .option('-t, --template