feat: website release v2 (#616)
* feat: upgrade to svelte 5 & tailwind v4 + add navigation menu to ui package * feat: website navigation with nav menu from shadcn * chore: refactor navigation menu storybook * fix: responsiveness bug * chore: remove tailwind config file * chore: - remove carbon icons from repo - add Geist font family * refactor website --------- Co-authored-by: Rotimi Best <[email protected]>
This commit is contained in:
co-authored by
Rotimi Best
parent
690cf147ff
commit
e6a3cf9332
Vendored
+1
-1
@@ -19,7 +19,7 @@
|
||||
"deno.importMap": "./supabase/functions/import_map.json",
|
||||
"editor.formatOnSave": true,
|
||||
"[svelte]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
"editor.defaultFormatter": "svelte.svelte-vscode"
|
||||
},
|
||||
"vitest.disableWorkspaceWarning": true,
|
||||
"typescript.tsserver.maxTsServerMemory": 4072,
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
# Folder Structure Documentation
|
||||
|
||||
This document describes the folder structure for the `apps/website` project, specifically focusing on the `src/lib` directory organization.
|
||||
|
||||
## Overview
|
||||
|
||||
The codebase follows a clear separation of concerns with three main categories:
|
||||
- **Components**: Presentational UI components (dummy components)
|
||||
- **Features**: Business logic and functional components
|
||||
- **Utils**: Utility functions, constants, and shared types
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
src/lib/
|
||||
├── components/ # UI Components (Presentational)
|
||||
│ ├── index.ts # Centralized exports - use this for imports
|
||||
│ ├── blog-list-item.svelte
|
||||
│ ├── core-feature-section.svelte
|
||||
│ ├── core-features.svelte
|
||||
│ ├── custom-img.svelte
|
||||
│ ├── faq.svelte
|
||||
│ ├── footer.svelte
|
||||
│ ├── hero.svelte
|
||||
│ ├── modal.svelte
|
||||
│ ├── more-features.svelte
|
||||
│ ├── navigation.svelte
|
||||
│ ├── not-found.svelte
|
||||
│ ├── page-header.svelte
|
||||
│ ├── page-signup-cta.svelte
|
||||
│ ├── senja-embed.svelte
|
||||
│ ├── student-dashboard.svelte
|
||||
│ ├── success.svelte
|
||||
│ ├── testimonial.svelte
|
||||
│ ├── timeline.svelte
|
||||
│ ├── tools-header.svelte
|
||||
│ └── upload-widget.svelte
|
||||
│
|
||||
├── features/ # Features (Business Logic)
|
||||
│ ├── contact/
|
||||
│ │ ├── feedback-form.svelte
|
||||
│ │ └── help-form.svelte
|
||||
│ └── tools/
|
||||
│ ├── progress/
|
||||
│ │ ├── avatar.svelte
|
||||
│ │ ├── background.svelte
|
||||
│ │ ├── download-button.svelte
|
||||
│ │ ├── full-view.svelte
|
||||
│ │ ├── mood.svelte
|
||||
│ │ ├── report.svelte
|
||||
│ │ └── store.ts
|
||||
│ ├── stopwatch/
|
||||
│ │ └── timer.svelte
|
||||
│ └── tic-tac-toe/
|
||||
│ ├── board.svelte
|
||||
│ ├── board.ts
|
||||
│ ├── store.ts
|
||||
│ └── winning-line.svelte
|
||||
│
|
||||
├── utils/ # Utilities
|
||||
│ ├── constants/
|
||||
│ │ └── tools.ts
|
||||
│ ├── posthog/
|
||||
│ │ └── index.ts
|
||||
│ ├── faqs.ts
|
||||
│ ├── format-date.ts
|
||||
│ ├── is-form-valid.ts
|
||||
│ ├── submit-form.ts
|
||||
│ └── types.ts
|
||||
│
|
||||
└── index.ts # Root index - re-exports from components
|
||||
│
|
||||
└── emojis/ # Assets (Static files)
|
||||
├── crossed-fingers.svg
|
||||
├── folder.svg
|
||||
├── globe.svg
|
||||
├── index.js
|
||||
├── left-fist.svg
|
||||
├── printer.svg
|
||||
├── right-fist.svg
|
||||
├── robot-arm.svg
|
||||
├── team.svg
|
||||
├── thumbs-up.svg
|
||||
└── ...
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Files and Folders
|
||||
- **Kebab-case**: All file and folder names use kebab-case (e.g., `blog-list-item.svelte`, `feedback-form.svelte`)
|
||||
- **Single-file folders**: If a folder contains only one file, it should be flattened to a single file (e.g., `Footer/Footer.svelte` → `components/footer.svelte`)
|
||||
|
||||
### Components
|
||||
- Component files use `.svelte` extension
|
||||
- Component names match their file names in kebab-case
|
||||
- Example: `blog-list-item.svelte` exports a component that can be imported as `BlogListItem`
|
||||
|
||||
## Directory Purposes
|
||||
|
||||
### `/components` - UI Components
|
||||
|
||||
**Purpose**: Presentational components with minimal or no business logic. These are "dummy" components that receive props and render UI.
|
||||
|
||||
**Characteristics**:
|
||||
- No API calls or complex state management
|
||||
- Primarily focused on rendering and styling
|
||||
- May have simple local state for UI interactions (e.g., open/close modals)
|
||||
- Reusable across different pages/features
|
||||
|
||||
**Examples**:
|
||||
- `footer.svelte` - Site footer component
|
||||
- `navigation.svelte` - Site navigation bar
|
||||
- `hero.svelte` - Hero section component
|
||||
- `modal.svelte` - Generic modal wrapper
|
||||
|
||||
**Import Pattern**:
|
||||
```svelte
|
||||
// ✅ CORRECT - Use named imports from the index
|
||||
import { Footer, Navigation, Hero } from '$lib/components';
|
||||
|
||||
// ❌ INCORRECT - Direct file imports are not allowed
|
||||
// import Footer from '$lib/components/footer.svelte';
|
||||
```
|
||||
|
||||
**Note**: All components must be imported using named imports from `$lib/components`. Direct file imports are not allowed.
|
||||
|
||||
### `/features` - Features
|
||||
|
||||
**Purpose**: Components and modules that contain business logic, API calls, form handling, state management, or domain-specific functionality.
|
||||
|
||||
**Characteristics**:
|
||||
- Contains business logic and data handling
|
||||
- May include API calls, form submissions, or data transformations
|
||||
- Often includes stores, utilities, or helper functions specific to the feature
|
||||
- Organized by feature domain (e.g., `contact/`, `tools/`)
|
||||
|
||||
**Examples**:
|
||||
- `features/contact/feedback-form.svelte` - Form with submission logic
|
||||
- `features/tools/progress/store.ts` - State management for progress tool
|
||||
- `features/tools/tic-tac-toe/board.ts` - Game logic
|
||||
|
||||
**Import Pattern**:
|
||||
```svelte
|
||||
import FeedbackForm from '$lib/features/contact/feedback-form.svelte';
|
||||
import { htmlBody, openModal } from '$lib/features/tools/progress/store';
|
||||
```
|
||||
|
||||
### `/utils` - Utilities
|
||||
|
||||
**Purpose**: Shared utility functions, constants, types, and helper modules used across the application.
|
||||
|
||||
**Characteristics**:
|
||||
- Pure functions or utility modules
|
||||
- No component-specific logic
|
||||
- Reusable across different features/components
|
||||
- May include TypeScript types and interfaces
|
||||
|
||||
**Examples**:
|
||||
- `utils/format-date.ts` - Date formatting utility
|
||||
- `utils/is-form-valid.ts` - Form validation helper
|
||||
- `utils/submit-form.ts` - Generic form submission utility
|
||||
- `utils/types.ts` - Shared TypeScript types
|
||||
- `utils/constants/tools.ts` - Application constants
|
||||
|
||||
**Import Pattern**:
|
||||
```typescript
|
||||
import { formatDate } from '$lib/utils/format-date';
|
||||
import { isFormValid } from '$lib/utils/is-form-valid';
|
||||
import type { BlogPost } from '$lib/utils/types';
|
||||
```
|
||||
|
||||
## Migration from Old Structure
|
||||
|
||||
### Old Structure (Before Refactoring)
|
||||
|
||||
```
|
||||
src/lib/
|
||||
├── Blog/BlogListItem.svelte
|
||||
├── Contact/FeedbackForm.svelte, HelpForm.svelte, Sucess.svelte
|
||||
├── Footer/Footer.svelte
|
||||
├── Home/*.svelte, faqs.ts
|
||||
├── Navigation/Navigation.svelte
|
||||
├── Modal/Modal.svelte
|
||||
├── PageHeader/PageHeader.svelte
|
||||
├── PageSignupCTA/index.svelte
|
||||
├── Senja/Embed.svelte
|
||||
├── Timeline/Timeline.svelte
|
||||
├── ToolsHeader/ToolsHeader.svelte
|
||||
├── UploadWidget/UploadWidget.svelte
|
||||
├── NotFound/NotFound.svelte
|
||||
├── custom/img.svelte, index.ts
|
||||
├── utils/*.ts
|
||||
└── types.ts
|
||||
```
|
||||
|
||||
### Key Changes
|
||||
|
||||
1. **Flattened single-file folders**: Folders with only one file were flattened (e.g., `Footer/Footer.svelte` → `components/footer.svelte`)
|
||||
|
||||
2. **Kebab-case conversion**: All PascalCase file names converted to kebab-case (e.g., `BlogListItem.svelte` → `blog-list-item.svelte`)
|
||||
|
||||
3. **Separation of concerns**: Components split into `components/` (UI) and `features/` (logic)
|
||||
|
||||
4. **Routes components moved**: Capitalized `components/` folders from `routes/tools/*/` moved to `features/tools/*/`
|
||||
|
||||
5. **Types moved**: `types.ts` moved to `utils/types.ts`
|
||||
|
||||
6. **Fixed typos**: `Sucess.svelte` → `success.svelte`
|
||||
|
||||
7. **Centralized component exports**: Created `components/index.ts` to export all components with PascalCase names
|
||||
|
||||
8. **Standardized imports**: All component imports now use named imports from `$lib/components` instead of direct file imports
|
||||
|
||||
## Import Examples
|
||||
|
||||
### Components
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
// ✅ CORRECT - Named imports from centralized index
|
||||
import { Footer, Navigation, Hero, Testimonial } from '$lib/components';
|
||||
|
||||
// ❌ INCORRECT - Direct file imports are not allowed
|
||||
// import Footer from '$lib/components/footer.svelte';
|
||||
</script>
|
||||
```
|
||||
|
||||
**Important**: All components are exported from `components/index.ts` with PascalCase names. Always use named imports from `$lib/components`.
|
||||
|
||||
### Features
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import FeedbackForm from '$lib/features/contact/feedback-form.svelte';
|
||||
import { htmlBody, openModal } from '$lib/features/tools/progress/store';
|
||||
import Board from '$lib/features/tools/tic-tac-toe/board.svelte';
|
||||
</script>
|
||||
```
|
||||
|
||||
### Utils
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { formatDate } from '$lib/utils/format-date';
|
||||
import { isFormValid } from '$lib/utils/is-form-valid';
|
||||
import { submitForm } from '$lib/utils/submit-form';
|
||||
import type { BlogPost } from '$lib/utils/types';
|
||||
import { tools } from '$lib/utils/constants/tools';
|
||||
</script>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **When to create a component vs feature**:
|
||||
- **Component**: If it's purely presentational and reusable
|
||||
- **Feature**: If it contains business logic, API calls, or domain-specific functionality
|
||||
|
||||
2. **File naming**:
|
||||
- Always use kebab-case for file and folder names
|
||||
- Match component file names with their import names (converted to PascalCase in imports)
|
||||
|
||||
3. **Component imports**:
|
||||
- **Always** import components using named imports from `$lib/components`
|
||||
- **Never** import directly from component files (e.g., `$lib/components/footer.svelte`)
|
||||
- All components are exported from `components/index.ts` with PascalCase names
|
||||
- Example: `import { Footer, Navigation } from '$lib/components'`
|
||||
|
||||
4. **Folder organization**:
|
||||
- Group related features in subfolders (e.g., `features/contact/`, `features/tools/progress/`)
|
||||
- Keep utility functions organized by purpose in `utils/`
|
||||
|
||||
5. **Avoid single-file folders**:
|
||||
- If a folder contains only one file, flatten it to a single file
|
||||
- Example: `Footer/Footer.svelte` → `components/footer.svelte`
|
||||
|
||||
6. **Type definitions**:
|
||||
- Shared types go in `utils/types.ts`
|
||||
- Feature-specific types can be co-located with the feature
|
||||
|
||||
7. **Adding new components**:
|
||||
- Add the component file in kebab-case (e.g., `new-component.svelte`)
|
||||
- Export it from `components/index.ts` with PascalCase name (e.g., `NewComponent`)
|
||||
- Import using: `import { NewComponent } from '$lib/components'`
|
||||
|
||||
## Routes Structure
|
||||
|
||||
Routes that previously had capitalized `components/` folders have been refactored:
|
||||
|
||||
**Before**:
|
||||
|
||||
```
|
||||
routes/tools/progress/components/Avatar.svelte
|
||||
routes/tools/stopwatch/components/Timer.svelte
|
||||
routes/tools/tic-tac-toe/components/Board.svelte
|
||||
```
|
||||
|
||||
**After**:
|
||||
|
||||
```
|
||||
lib/features/tools/progress/avatar.svelte
|
||||
lib/features/tools/stopwatch/timer.svelte
|
||||
lib/features/tools/tic-tac-toe/board.svelte
|
||||
```
|
||||
|
||||
Routes now import from `$lib/features/tools/*/` instead of `./components/`.
|
||||
|
||||
## Assets
|
||||
|
||||
Static assets like images, SVGs, and other media files remain in their respective locations:
|
||||
- `emojis/` - Emoji SVG files
|
||||
- `public/` - Public static assets (images, fonts, etc.)
|
||||
|
||||
## Component Index File
|
||||
|
||||
All components are exported from `components/index.ts` with PascalCase names:
|
||||
|
||||
```typescript
|
||||
// components/index.ts
|
||||
export { default as BlogListItem } from './blog-list-item.svelte';
|
||||
export { default as Footer } from './footer.svelte';
|
||||
export { default as Navigation } from './navigation.svelte';
|
||||
// ... etc
|
||||
```
|
||||
|
||||
This allows for clean, consistent imports:
|
||||
```svelte
|
||||
import { Footer, Navigation, Hero } from '$lib/components';
|
||||
```
|
||||
|
||||
**Rule**: Direct file imports (e.g., `import Footer from '$lib/components/footer.svelte'`) are **not allowed**. Always use named imports from `$lib/components`.
|
||||
|
||||
## Summary
|
||||
|
||||
This structure provides:
|
||||
- ✅ Clear separation between UI and business logic
|
||||
- ✅ Consistent naming conventions (kebab-case)
|
||||
- ✅ Flattened structure (no unnecessary single-file folders)
|
||||
- ✅ Centralized component exports via `index.ts`
|
||||
- ✅ Standardized import pattern (named imports from `$lib/components`)
|
||||
- ✅ Easy to navigate and maintain
|
||||
- ✅ Scalable for future growth
|
||||
|
||||
For questions or suggestions about this structure, please refer to the project maintainers.
|
||||
|
||||
+16
-13
@@ -12,18 +12,18 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"carbon-icons-svelte": "^12.3.0",
|
||||
"@sveltejs/kit": "^2.5.27",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||
"mdsvex": "^0.11.0",
|
||||
"postcss": "^8.4.29",
|
||||
"svelte": "^4.1.2",
|
||||
"svelte-check": "^3.4.3",
|
||||
"svelte-sequential-preprocessor": "^2.0.1",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.4.4"
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"svelte": "^5.0.0",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"typescript": "^5.5.0",
|
||||
"vite": "^5.4.4",
|
||||
"@cio/tsconfig": "workspace:*",
|
||||
"@sveltejs/adapter-vercel": "^4.0.0"
|
||||
},
|
||||
"type": "module",
|
||||
"engines": {
|
||||
@@ -36,8 +36,11 @@
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-toc": "^9.0.0",
|
||||
"remark-unwrap-images": "^4.0.0",
|
||||
"@cio/utils": "workspace:*",
|
||||
"shiki": "^0.14.7",
|
||||
"svelte-meta-tags": "^3.1.2"
|
||||
"svelte-meta-tags": "^3.1.2",
|
||||
"@tailwindcss/postcss": "^4.1.17",
|
||||
"@lucide/svelte": "^0.545.0",
|
||||
"@cio/ui": "workspace:*",
|
||||
"@cio/utils": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
'@tailwindcss/postcss': {}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import url('https://fonts.googleapis.com/css2?family=Geist:[email protected]&display=swap');
|
||||
|
||||
@import 'tailwindcss';
|
||||
|
||||
@import '@cio/ui/output.css';
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
li {
|
||||
font-family: 'Inter', sans-serif;
|
||||
* {
|
||||
font-family: 'Geist', sans-serif;
|
||||
}
|
||||
|
||||
.floating {
|
||||
@@ -21,6 +25,7 @@ li {
|
||||
-webkit-transform: translateY(0rem);
|
||||
transform: translateY(0rem);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: translateY(1rem);
|
||||
transform: translateY(1rem);
|
||||
@@ -33,34 +38,42 @@ li {
|
||||
-webkit-transform: translateY(0rem);
|
||||
transform: translateY(0rem);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: translateY(1rem);
|
||||
transform: translateY(1rem);
|
||||
}
|
||||
}
|
||||
|
||||
.prose {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-family: 'Geist', sans-serif;
|
||||
}
|
||||
|
||||
.prose :is(h1, h2, h3, h4, h5, h6) {
|
||||
font-weight: 700;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.prose :is(h1) {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.prose :is(h2) {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.prose :is(h3) {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
|
||||
.prose :is(h4) {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.prose :is(h5) {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
|
||||
.prose :is(h6) {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
@@ -82,6 +95,7 @@ li {
|
||||
border-radius: 8px;
|
||||
tab-size: 2;
|
||||
}
|
||||
|
||||
.prose blockquote {
|
||||
border-left: 4px solid gray;
|
||||
font-style: italic;
|
||||
@@ -89,6 +103,7 @@ li {
|
||||
padding-left: 16px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.prose figure + em {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
@@ -105,11 +120,13 @@ li {
|
||||
.tools-hero-bg {
|
||||
background-image: url('https://assets.cdn.clsrio.com/www/tools-hero-bg.svg');
|
||||
}
|
||||
|
||||
@media (min-width: 760px) {
|
||||
.tools-hero-bg {
|
||||
min-height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
.cio-bg-blue {
|
||||
background: rgb(2, 51, 189);
|
||||
background-size: cover;
|
||||
@@ -122,8 +139,20 @@ li {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
svg.lucide-icon:not(.custom) {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.dark svg.lucide-icon:not(.custom) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.embed {
|
||||
width: 560px;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
--primary: var(--color-blue-700);
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<script>
|
||||
import Renew from 'carbon-icons-svelte/lib/Renew.svelte';
|
||||
export let label = '';
|
||||
export let className = '';
|
||||
export let isLoading = false;
|
||||
export let isDisabled = false;
|
||||
export let onClick = () => {};
|
||||
</script>
|
||||
|
||||
<div class="w-full flex items-center justify-center pt-5">
|
||||
<button
|
||||
class="bg-[#0233BD] text-white font-medium text-sm py-4 {className}"
|
||||
disabled={isDisabled}
|
||||
on:click={onClick}
|
||||
>
|
||||
{#if isLoading}
|
||||
<Renew size={24} class="animate-spin" />
|
||||
{:else}
|
||||
{label}
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let label: string;
|
||||
</script>
|
||||
|
||||
<p class="whitespace-nowrap bg-gray-200 px-2 py-1 rounded-full text-xs font-normal text-black">
|
||||
{label}
|
||||
</p>
|
||||
@@ -1,70 +0,0 @@
|
||||
<script>
|
||||
import Button from '$lib/Button/Button.svelte';
|
||||
import TextArea from '$lib/Input/TextArea.svelte';
|
||||
import TextField from '$lib/Input/TextField.svelte';
|
||||
import { isFormValid } from '$lib/utils/isFormValid';
|
||||
import { submitForm } from '$lib/utils/submitForm';
|
||||
import Sucess from '$lib/Contact/Sucess.svelte';
|
||||
|
||||
let name = '';
|
||||
let email = '';
|
||||
let title = '';
|
||||
let description = '';
|
||||
|
||||
let isLoading = false;
|
||||
let errorMessage = '';
|
||||
let sent = false;
|
||||
|
||||
const handleClick = async () => {
|
||||
const data = { name, email, title, description };
|
||||
|
||||
if (!isFormValid(data)) {
|
||||
errorMessage = 'Please fill out all required fields.';
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading = true;
|
||||
|
||||
const formData = {
|
||||
title: `[FEEDBACK] ${title}`,
|
||||
description: `My name is ${JSON.stringify(name)}, email is ${JSON.stringify(
|
||||
email
|
||||
)}. \n\n${JSON.stringify(description)}.`,
|
||||
replyTo: email
|
||||
};
|
||||
|
||||
try {
|
||||
await submitForm(formData);
|
||||
sent = true;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if sent}
|
||||
<Sucess />
|
||||
{:else}
|
||||
<div class="w-full my-10 space-y-5">
|
||||
<div class="flex flex-col md:flex-row gap-10">
|
||||
<TextField label="Your Name" isRequired className="w-full" bind:value={name} />
|
||||
<TextField label="Your Email" isRequired className="w-full" bind:value={email} />
|
||||
</div>
|
||||
<TextField label="Subject" isRequired bind:value={title} />
|
||||
<TextArea label="Message" isRequired bind:value={description} />
|
||||
{#if !!errorMessage}
|
||||
<p class="text-left text-sm text-red-500">{errorMessage}</p>
|
||||
{/if}
|
||||
<Button
|
||||
label="Submit"
|
||||
className="px-32"
|
||||
onClick={handleClick}
|
||||
isDisabled={isLoading}
|
||||
{isLoading}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,70 +0,0 @@
|
||||
<script>
|
||||
import Button from '$lib/Button/Button.svelte';
|
||||
import TextArea from '$lib/Input/TextArea.svelte';
|
||||
import TextField from '$lib/Input/TextField.svelte';
|
||||
import { isFormValid } from '$lib/utils/isFormValid';
|
||||
import { submitForm } from '$lib/utils/submitForm';
|
||||
import Sucess from './Sucess.svelte';
|
||||
|
||||
let name = '';
|
||||
let email = '';
|
||||
let title = '';
|
||||
let description = '';
|
||||
|
||||
let isLoading = false;
|
||||
let errorMessage = '';
|
||||
let sent = false;
|
||||
|
||||
async function handleClick() {
|
||||
const data = { name, email, title, description };
|
||||
|
||||
if (!isFormValid(data)) {
|
||||
errorMessage = 'Please fill out all required fields.';
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading = true;
|
||||
|
||||
const formData = {
|
||||
title: `[HELP] ${title}`,
|
||||
description: `My name is ${JSON.stringify(name)}, email is ${JSON.stringify(
|
||||
email
|
||||
)}. \n\n${JSON.stringify(description)}.`,
|
||||
replyTo: email
|
||||
};
|
||||
|
||||
try {
|
||||
await submitForm(formData);
|
||||
sent = true;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if sent}
|
||||
<Sucess />
|
||||
{:else}
|
||||
<div class="w-full my-10 space-y-5">
|
||||
<div class="flex flex-col md:flex-row gap-10">
|
||||
<TextField label="Your Name" isRequired className="w-full" bind:value={name} />
|
||||
<TextField label="Your Email" isRequired className="w-full" bind:value={email} />
|
||||
</div>
|
||||
<TextField label="Subject" isRequired bind:value={title} />
|
||||
<TextArea label="Message" isRequired bind:value={description} />
|
||||
{#if !!errorMessage}
|
||||
<p class="text-left text-sm text-red-500">{errorMessage}</p>
|
||||
{/if}
|
||||
<Button
|
||||
label="Submit"
|
||||
className="px-32"
|
||||
onClick={handleClick}
|
||||
isDisabled={isLoading}
|
||||
{isLoading}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,213 +0,0 @@
|
||||
<script>
|
||||
import LogoYoutube from 'carbon-icons-svelte/lib/LogoYoutube.svelte';
|
||||
import LogoFacebook from 'carbon-icons-svelte/lib/LogoFacebook.svelte';
|
||||
</script>
|
||||
|
||||
<div class="border-t-[1px] px-[2%] lg:px-[14%]">
|
||||
<div class="flex flex-col items-center justify-between py-[10%] pl-5 lg:flex-row lg:pl-0">
|
||||
<div class="w-full min-w-[200px] lg:w-3/12">
|
||||
<img
|
||||
width="32"
|
||||
height="32"
|
||||
loading="lazy"
|
||||
src="/logo-192.png"
|
||||
alt="classroomio logo"
|
||||
class="mb-5"
|
||||
/>
|
||||
<p class="lg:text-base mb-5 mr-2 text-sm leading-7 tracking-wide text-gray-600 lg:leading-7">
|
||||
The Open Source Learning Management System for Companies
|
||||
<br />
|
||||
ClassroomIO is the easiest way to grow your teaching business.
|
||||
</p>
|
||||
<!-- <a
|
||||
href="https://www.producthunt.com/posts/classroomio?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-classroomio"
|
||||
target="_blank"
|
||||
><img
|
||||
loading="lazy"
|
||||
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=425236&theme=light"
|
||||
alt="ClassroomIO - The Open Source Platform for Tech Bootcamps | Product Hunt"
|
||||
style="width: 250px; height: 54px;"
|
||||
width="250"
|
||||
height="54"
|
||||
/></a
|
||||
> -->
|
||||
<div class="mt-5 flex items-center gap-3 lg:mt-10 lg:gap-7">
|
||||
<a href="https://www.facebook.com/classroomiohq" target="_blank" aria-label="Facebook Link">
|
||||
<LogoFacebook
|
||||
class="scale-90 cursor-pointer fill-gray-400 transition-all hover:scale-110 hover:fill-gray-500 lg:scale-150"
|
||||
size={20}
|
||||
/>
|
||||
</a>
|
||||
<a href="https://twitter.com/classroomio" target="_blank" aria-label="Twitter Link">
|
||||
<img
|
||||
width="20"
|
||||
height="20"
|
||||
loading="lazy"
|
||||
src="/twitter.svg"
|
||||
alt=""
|
||||
class="scale-90 cursor-pointer transition-all hover:scale-110 hover:fill-gray-500 lg:scale-150"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/@ClassroomIO" target="_blank" aria-label="Youtube Link">
|
||||
<LogoYoutube
|
||||
class="scale-90 cursor-pointer fill-gray-400 transition-all hover:scale-110 hover:fill-gray-500 lg:scale-150"
|
||||
size={20}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav
|
||||
class="mt-16 flex w-full min-w-[400px] flex-col items-start justify-end gap-12 space-y-6 lg:mt-0 lg:w-3/4 lg:flex-row lg:space-y-0"
|
||||
>
|
||||
<div class="max-w-[120px]">
|
||||
<p class="lg:text-base mb-3 text-base font-semibold leading-6 text-gray-900 lg:mb-10">
|
||||
Product
|
||||
</p>
|
||||
<ul class="space-y-2 lg:space-y-4">
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/#coursemanagement"> Course Management </a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/#customization"> Customizations </a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/#collaboration"> Forum </a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/#ai"> AI Lesson Planner </a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/#student-dashboard"> Student Dashboard </a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="max-w-[120px]">
|
||||
<p class="lg:text-base mb-3 text-base font-semibold leading-6 text-gray-900 lg:mb-10">
|
||||
Solutions
|
||||
</p>
|
||||
<ul class="space-y-2 lg:space-y-4">
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/employee-training">Employee Training</a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/bootcamps">Bootcamps</a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/customer-education">Customer Education</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="max-w-[120px]">
|
||||
<p class="lg:text-base mb-3 text-base font-semibold leading-6 text-gray-900 lg:mb-10">
|
||||
Resources
|
||||
</p>
|
||||
<ul class="space-y-2 lg:space-y-4">
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/contact">Contact us</a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/teach">Teach with us</a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a target="_blank" href="https://classroomio.com/docs/"> Help center </a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a target="_blank" href="https://classroomio.com/docs/quickstart/signup">
|
||||
Getting Started
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/oss-friends"> OSS Friends </a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="max-w-[120px]">
|
||||
<p class="lg:text-base mb-3 text-base font-semibold leading-6 text-gray-900 lg:mb-10">
|
||||
Company
|
||||
</p>
|
||||
<ul class="space-y-2 lg:space-y-4">
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="/roadmap"> Roadmap </a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a href="mailto:[email protected]" target="_blank"> Support </a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a
|
||||
href="https://app.enzuzo.com/policies/privacy/958fc978-5477-11ee-a03b-7b111830c594"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy policy
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a target="_blank" href="https://classroomio.com/docs/quickstart/signup">
|
||||
Getting Started
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
class="lg:text-base text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<a
|
||||
href="https://app.enzuzo.com/policies/tos/958fc978-5477-11ee-a03b-7b111830c594"
|
||||
target="_blank"
|
||||
>
|
||||
Terms of use
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<footer
|
||||
class="lg:text-base border-t-[1px] py-[3%] text-center text-xs font-light leading-5 text-gray-500 lg:text-left"
|
||||
>
|
||||
<p>All rights reserved.</p>
|
||||
<p class="flex items-center">
|
||||
ClassroomIO © {new Date().getFullYear()} by
|
||||
<a href="https://twitter.com/rotimi_best" target="_blank" class="mx-1 text-blue-700 underline"
|
||||
>Rotimi Best</a
|
||||
>
|
||||
from
|
||||
<img loading="lazy" alt="Nigerian Flag" src="/ng.png" class="ml-2 h-3 w-3" />
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -1,42 +0,0 @@
|
||||
<script>
|
||||
import { faqs } from './faqs';
|
||||
import PageSignupCTA from '$lib/PageSignupCTA/index.svelte';
|
||||
</script>
|
||||
|
||||
<div class="mb-[15%] mt-[5%]">
|
||||
<header class="mb-[5%] px-[8%] text-center">
|
||||
<h1 class="mt-2 text-4xl font-medium tracking-tight text-gray-900 lg:text-4xl">
|
||||
Questions & Answers
|
||||
</h1>
|
||||
<p
|
||||
class="mt-4 text-lg font-normal tracking-normal text-gray-600 lg:px-[20%] lg:text-xl lg:tracking-wide"
|
||||
>
|
||||
Can’t find the answer you’re looking for?
|
||||
<a class="font-semibold text-blue-700" href="mailto:[email protected]" target="_blank"
|
||||
>Shoot us an email</a
|
||||
> and we’ll get back to you ASAP.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="mt-[20%] grid grid-cols-1 gap-10 lg:m-0 lg:grid-cols-3 lg:gap-20 lg:px-[13%]">
|
||||
{#each faqs as faq}
|
||||
<div class="max-w-[100%] px-6 lg:p-0">
|
||||
<h1 class="text:md lg:text-base font-semibold leading-8 text-gray-800">
|
||||
{faq.question}
|
||||
</h1>
|
||||
<p
|
||||
class="text:md lg:text-base mt-2 font-light leading-7 tracking-wide text-gray-600 lg:leading-9"
|
||||
>
|
||||
{faq.answer}
|
||||
</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<PageSignupCTA
|
||||
header="Your Custom Academy – Up & Running in Minutes!"
|
||||
subText="Try before you buy. No credit card required."
|
||||
btnLabel="Book a demo"
|
||||
link="classroomio/demo"
|
||||
/>
|
||||
</div>
|
||||
@@ -1,28 +0,0 @@
|
||||
<script>
|
||||
import SenjaEmbed from '$lib/Senja/Embed.svelte';
|
||||
</script>
|
||||
|
||||
<section id="testimonial" class="flex flex-col items-center py-[5%] px-3 lg:px-0">
|
||||
<h1
|
||||
class="mx-auto text-3xl md:text-4xl lg:text-6xl font-normal leading-[1.5] flex flex-col items-center gap-4"
|
||||
>
|
||||
Hear from Happy ClassroomIO Users
|
||||
</h1>
|
||||
|
||||
<p
|
||||
class="mx-auto text-center my-5 lg:mt-6 px-4 lg:p-0 text-base tracking-wide leading-6 text-gray-400 lg:w-2/4"
|
||||
>
|
||||
See what excites them about building and managing bootcamps with ClassroomIO.
|
||||
</p>
|
||||
<a
|
||||
class="md:text-base w-11/12 rounded-md bg-blue-700 px-6 py-3 text-sm font-medium text-white text-center transition-all delay-100 hover:bg-blue-800 hover:shadow-xl hover:scale-95 md:w-fit lg:px-6 lg:py-5 lg:font-semibold"
|
||||
href="/signup"
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
Sign Up for Free
|
||||
</a>
|
||||
|
||||
<div class="relative my-5 md:my-10 w-[85%] lg:w-[70%]">
|
||||
<SenjaEmbed id="3e847d7a-13d3-483e-8a71-5b35273dcc6c" />
|
||||
</div>
|
||||
</section>
|
||||
@@ -1,17 +0,0 @@
|
||||
<script>
|
||||
export let color = 'currentColor';
|
||||
</script>
|
||||
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="dark"
|
||||
>
|
||||
<path
|
||||
d="M18 22.5H6C5.60232 22.4995 5.22105 22.3414 4.93984 22.0602C4.65864 21.7789 4.50046 21.3977 4.5 21V3C4.5004 2.6023 4.65856 2.221 4.93978 1.93978C5.221 1.65856 5.6023 1.5004 6 1.5H18C18.3977 1.50046 18.779 1.65864 19.0602 1.93984C19.3414 2.22105 19.4995 2.60232 19.5 3V15.4636L15.75 13.5886L12 15.4636V3H6V21H18V18H19.5V21C19.4995 21.3977 19.3413 21.7789 19.0601 22.0601C18.7789 22.3413 18.3977 22.4995 18 22.5ZM15.75 11.9114L18 13.0364V3H13.5V13.0364L15.75 11.9114Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,56 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let label = '';
|
||||
export let disabled = false;
|
||||
export let placeholder = '';
|
||||
export let value = '';
|
||||
export let rows = 3;
|
||||
export let className = '';
|
||||
export let labelClassName = '';
|
||||
export let bgColor = 'bg-gray-50 focus:bg-primary-50 dark:bg-neutral-700 dark:text-white';
|
||||
export let helperMessage = '';
|
||||
export let errorMessage = '';
|
||||
export let isRequired = false;
|
||||
export let onChange = () => {}; // This is to know if element is 'dirty'
|
||||
export let ref = null;
|
||||
</script>
|
||||
|
||||
<label for="text-field" class="block w-full relative {className}">
|
||||
{#if label}
|
||||
<p class="dark:text-white text-left w-full flex items-center justify-between {labelClassName}">
|
||||
<span>
|
||||
{label}
|
||||
{#if isRequired}
|
||||
<span class="text-red-700">*</span>
|
||||
{/if}
|
||||
</span>
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<textarea
|
||||
{disabled}
|
||||
{rows}
|
||||
{placeholder}
|
||||
bind:value
|
||||
bind:this={ref}
|
||||
required={isRequired}
|
||||
class="form-textarea p-2 border-l-0 border-r-0 border-t-0 border-b-2 border-gray-200 dark:border-neutral-600 focus:border-l-0 focus:border-r-0 rounded-t-md focus:border-t-0 focus:border-b-2 focus:border-primary-600 mt-1 block w-full
|
||||
{bgColor} {!!errorMessage ? 'border-red-500' : 'border-gray-300'}"
|
||||
on:change={onChange}
|
||||
/>
|
||||
{#if !!errorMessage}
|
||||
<p class="text-sm text-red-500">
|
||||
{errorMessage}
|
||||
</p>
|
||||
{:else if helperMessage}
|
||||
<p class="text-sm text-gray-500 dark:text-white">
|
||||
{helperMessage}
|
||||
</p>
|
||||
{/if}
|
||||
<slot name="iconbutton" />
|
||||
</label>
|
||||
|
||||
<style>
|
||||
.form-textarea {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,81 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let label = '';
|
||||
export let placeholder = '';
|
||||
export let value: string | number | null = null;
|
||||
export let name = '';
|
||||
export let className = '';
|
||||
export let inputClassName = '';
|
||||
export let labelClassName = '';
|
||||
export let bgColor = 'bg-gray-100 focus:bg-primary-50 dark:bg-neutral-700 dark:text-white';
|
||||
export let autoFocus = false;
|
||||
export let isRequired = false;
|
||||
export let isDisabled = false;
|
||||
export let errorMessage = '';
|
||||
export let helperMessage = '';
|
||||
export let autoComplete = true;
|
||||
|
||||
let ref: HTMLInputElement | undefined = undefined;
|
||||
let focusClass = '';
|
||||
|
||||
onMount(() => {
|
||||
if (autoFocus && ref) {
|
||||
ref.focus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<label class="block relative {className}">
|
||||
{#if label}
|
||||
<p class=" dark:text-white text-left m-0 {labelClassName}">
|
||||
{label}
|
||||
{#if isRequired}
|
||||
<span class="text-red-700">*</span>
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<input
|
||||
class="form-input border-l-0 border-r-0 border-t-0 border-b-2 border-gray-200 dark:border-neutral-600 focus:border-l-0 focus:border-r-0 rounded-t-md focus:border-t-0 focus:border-b-2 {inputClassName} {focusClass} {isDisabled &&
|
||||
'hover:cursor-not-allowed opacity-50'} dark:text-black p-2 mt-1 block w-full {bgColor} {errorMessage
|
||||
? 'border-red-600'
|
||||
: ''}"
|
||||
{placeholder}
|
||||
bind:value
|
||||
bind:this={ref}
|
||||
required={isRequired}
|
||||
disabled={isDisabled}
|
||||
autocomplete={autoComplete ? 'on' : 'off'}
|
||||
on:blur={(e) => {
|
||||
if (focusClass.includes('border-primary-600')) {
|
||||
focusClass = focusClass.replace('border-primary-600', '');
|
||||
}
|
||||
}}
|
||||
on:focus={() => (focusClass += ' border-primary-600')}
|
||||
{name}
|
||||
/>
|
||||
{#if errorMessage}
|
||||
<p class="text-sm text-red-500">{errorMessage}</p>
|
||||
{:else if helperMessage}
|
||||
<p class="dark:text-white text-sm">
|
||||
{helperMessage}
|
||||
</p>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<style>
|
||||
.form-input {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.password-eye {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 6px;
|
||||
}
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,334 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import CourseIcon from '$lib/Icons/CourseIcon.svelte';
|
||||
import ChevronDown from 'carbon-icons-svelte/lib/ChevronDown.svelte';
|
||||
import CloseLarge from 'carbon-icons-svelte/lib/CloseLarge.svelte';
|
||||
import ForumIcon from 'carbon-icons-svelte/lib/Forum.svelte';
|
||||
import MapCenter from 'carbon-icons-svelte/lib/MapCenter.svelte';
|
||||
import TextAlignJustify from 'carbon-icons-svelte/lib/TextAlignJustify.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { fly } from 'svelte/transition';
|
||||
|
||||
export let stars: number;
|
||||
|
||||
let showDrawer = false;
|
||||
let showSolutions = false;
|
||||
let activeLink = '';
|
||||
let closeTimeout: NodeJS.Timeout;
|
||||
|
||||
function closeSolutions() {
|
||||
closeTimeout = setTimeout(() => {
|
||||
showSolutions = false;
|
||||
}, 100);
|
||||
}
|
||||
function resetCloseTimeout() {
|
||||
clearTimeout(closeTimeout);
|
||||
}
|
||||
|
||||
function handleShowDrawer() {
|
||||
showDrawer = !showDrawer;
|
||||
}
|
||||
|
||||
function handleShowSolutions() {
|
||||
showSolutions = !showSolutions;
|
||||
}
|
||||
|
||||
const solutions = [
|
||||
{
|
||||
key: 'employee-training',
|
||||
title: 'Employee Training',
|
||||
subtitle: 'Keep your team in sync.'
|
||||
},
|
||||
{
|
||||
key: 'bootcamps',
|
||||
title: 'Bootcamps',
|
||||
subtitle: 'Drive student satisfaction.'
|
||||
},
|
||||
{
|
||||
key: 'customer-education',
|
||||
title: 'Customer Education',
|
||||
subtitle: 'Teach customers your product.'
|
||||
}
|
||||
];
|
||||
|
||||
$: activeLink = page.url.pathname;
|
||||
$: activeHash = page.url.hash;
|
||||
$: isSolutionsActive = solutions.some((s) => activeHash.includes(s.key));
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex w-full justify-between items-center py-6 border-b-[1px] md:px-12 px-5 fixed top-0 z-[3000] filter backdrop-blur-xl shadow-sm bg-white"
|
||||
>
|
||||
<a href="/" class="w-[10%]">
|
||||
<div class="flex items-center w-full">
|
||||
<img
|
||||
loading="lazy"
|
||||
width="28"
|
||||
height="28"
|
||||
src="/logo-512.png"
|
||||
alt="classroomio logo"
|
||||
class="w-6 md:w-10"
|
||||
/>
|
||||
<h1 class="text-xs md:text-lg font-medium ml-2">ClassroomIO</h1>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<nav class="w-[50%] hidden md:hidden lg:block">
|
||||
<ul class="flex justify-center items-center w-full gap-5">
|
||||
<li class="text-gray-800 font-semibold text-sm cursor-pointer relative">
|
||||
<button
|
||||
on:focus={() => (showSolutions = true)}
|
||||
on:mouseenter={() => (showSolutions = true)}
|
||||
on:mouseleave={closeSolutions}
|
||||
class="flex items-center hover:bg-gray-100 {showSolutions &&
|
||||
'bg-gray-100'} px-4 py-2 rounded-md transition-all duration-200"
|
||||
on:click={() => (showSolutions = !showSolutions)}
|
||||
class:active={isSolutionsActive}
|
||||
>
|
||||
<span class="mr-2">Solutions</span>
|
||||
<ChevronDown size={16} />
|
||||
</button>
|
||||
{#if showSolutions}
|
||||
<button
|
||||
class="absolute w-[20rem] top-10 -left-10 border rounded-lg px-2 py-4 shadow-slate-700 z-[3001] bg-white flex flex-col gap-3"
|
||||
on:mouseenter={resetCloseTimeout}
|
||||
on:mouseleave={() => (showSolutions = false)}
|
||||
>
|
||||
{#each solutions as solution}
|
||||
<a
|
||||
class="flex justify-between items-center w-full rounded-lg hover:bg-gray-100 hover:cursor-pointer p-2 transition-colors duration-200"
|
||||
href="/{solution.key}"
|
||||
on:click={() => (showSolutions = false)}
|
||||
>
|
||||
{#if solution.key === 'employee-training'}
|
||||
<CourseIcon />
|
||||
{:else if solution.key === 'bootcamps'}
|
||||
<MapCenter size={24} />
|
||||
{:else if solution.key === 'customer-education'}
|
||||
<ForumIcon size={24} />
|
||||
{/if}
|
||||
|
||||
<div class="w-[84%] text-start">
|
||||
<h3 class="font-semibold text-sm text-gray-700">
|
||||
{solution.title}
|
||||
</h3>
|
||||
<p class="font-normal text-sm text-gray-600">
|
||||
{solution.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</button>
|
||||
{/if}
|
||||
</li>
|
||||
<a
|
||||
href="/tools"
|
||||
class="text-gray-800 font-semibold text-sm cursor-pointer"
|
||||
class:active={activeLink.startsWith('/tools')}
|
||||
>
|
||||
<li class="hover:bg-gray-100 px-4 py-2 rounded-md transition-all duration-200">
|
||||
Free Tools
|
||||
</li>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/blog"
|
||||
class="text-gray-800 font-semibold text-sm cursor-pointer"
|
||||
class:active={activeLink.startsWith('/blog')}
|
||||
>
|
||||
<li class="hover:bg-gray-100 px-4 py-2 rounded-md transition-all duration-200">Blog</li>
|
||||
</a>
|
||||
<a
|
||||
href="/pricing"
|
||||
class="text-gray-800 font-semibold text-sm cursor-pointer"
|
||||
class:active={activeLink.startsWith('/pricing')}
|
||||
>
|
||||
<li class="hover:bg-gray-100 px-4 py-2 rounded-md transition-all duration-200">Pricing</li>
|
||||
</a>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="justify-between items-center flex-row hidden md:hidden lg:flex gap-3">
|
||||
<a
|
||||
href="/discord"
|
||||
target="_blank"
|
||||
class="flex items-center hover:opacity-80 transition-opacity duration-200"
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
alt="discord logo"
|
||||
src="/discord-blue.png"
|
||||
class="w-6 h-5 cursor-pointer"
|
||||
/>
|
||||
</a>
|
||||
<div class="flex items-center">
|
||||
<a
|
||||
href="/github"
|
||||
target="_blank"
|
||||
class="flex items-center gap-1.5 p-2 rounded-md transition-all duration-200 ease-in-out hover:bg-gray-100 group"
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
alt="github logo"
|
||||
src="/github-mark.png"
|
||||
class="w-5 h-5 cursor-pointer transition-transform duration-200 group-hover:scale-110"
|
||||
/>
|
||||
<span
|
||||
class="text-sm text-gray-600 font-medium leading-none transition-colors duration-200 group-hover:text-black"
|
||||
>
|
||||
{stars}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
class="font-medium text-sm after:content-['→'] after:ml-2 hover:opacity-80 transition-opacity duration-200"
|
||||
href="https://app.classroomio.com"
|
||||
>
|
||||
Dashboard
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Hamburger Menu"
|
||||
class="block md:block lg:hidden"
|
||||
on:click={handleShowSolutions}
|
||||
>
|
||||
<TextAlignJustify size={24} />
|
||||
</button>
|
||||
|
||||
{#if showSolutions}
|
||||
<div
|
||||
in:fly={{ x: 20, duration: 700 }}
|
||||
out:fly={{ x: 20, duration: 400 }}
|
||||
class="w-[60%] md:w-[40%] h-[100vh] border-2 sm:block lg:hidden px-3 pt-3 pb-2 md:p-7 absolute right-0 top-0 bg-white"
|
||||
>
|
||||
<div class="flex justify-between py-2 mb-5">
|
||||
<img
|
||||
loading="lazy"
|
||||
width="20"
|
||||
height="20"
|
||||
src="/logo-512.png"
|
||||
alt="classroomio logo"
|
||||
class="w-[15%]"
|
||||
/>
|
||||
<button class="mr-5" on:click={handleShowSolutions}>
|
||||
<CloseLarge size={24} />
|
||||
</button>
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="flex items-center flex-col lg:flex-row justify-between w-full">
|
||||
<li class="text-gray-800 font-semibold text-sm md:text-lg cursor-pointer w-full">
|
||||
<button
|
||||
class="w-full flex items-center justify-between hover:bg-gray-100 py-3 px-4 rounded-lg transition-all duration-200"
|
||||
on:click={handleShowDrawer}
|
||||
class:active={isSolutionsActive}
|
||||
>
|
||||
Our Superpowers <ChevronDown />
|
||||
</button>
|
||||
{#if showDrawer}
|
||||
<div in:fly={{ y: -20, duration: 700 }} out:fly={{ y: 20, duration: 400 }}>
|
||||
{#each solutions as solution}
|
||||
<a
|
||||
href="/{solution.key}"
|
||||
on:click={() => {
|
||||
handleShowSolutions();
|
||||
}}
|
||||
>
|
||||
<p
|
||||
class="font-normal text-xs text-gray-700 hover:bg-gray-100 rounded-lg py-2.5 pl-5 transition-colors duration-200"
|
||||
>
|
||||
{solution.title}
|
||||
</p>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</li>
|
||||
<a
|
||||
class="text-gray-800 font-semibold text-sm md:text-lg cursor-pointer hover:bg-gray-100 py-3 px-4 rounded-xl w-full transition-all duration-200"
|
||||
on:click={handleShowSolutions}
|
||||
href="/tools"
|
||||
>
|
||||
<li>Free Tools</li>
|
||||
</a>
|
||||
<a
|
||||
class="text-gray-800 font-semibold text-sm md:text-lg cursor-pointer hover:bg-gray-100 py-3 px-4 rounded-md w-full transition-all duration-200"
|
||||
on:click={() => {
|
||||
handleShowSolutions();
|
||||
}}
|
||||
class:active={activeLink.startsWith('/blog')}
|
||||
href="/blog"
|
||||
>
|
||||
<li>Blog</li>
|
||||
</a>
|
||||
<a
|
||||
class="text-gray-800 font-semibold text-sm md:text-lg cursor-pointer hover:bg-gray-100 py-3 px-4 rounded-xl w-full transition-all duration-200"
|
||||
on:click={() => {
|
||||
handleShowSolutions();
|
||||
}}
|
||||
href="/pricing"
|
||||
class:active={activeLink.startsWith('/pricing')}
|
||||
>
|
||||
<li>Pricing</li>
|
||||
</a>
|
||||
<a
|
||||
class="text-gray-800 font-semibold text-sm md:text-lg cursor-pointer hover:bg-gray-100 py-3 px-4 rounded-md w-full transition-all duration-200"
|
||||
on:click={() => {
|
||||
handleShowSolutions();
|
||||
}}
|
||||
class:active={activeHash.includes('morefeatures')}
|
||||
href="/#morefeatures"
|
||||
>
|
||||
<li>More features</li>
|
||||
</a>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="flex items-start flex-col gap-y-2 mt-5 border-t-[1px] pt-5">
|
||||
<a
|
||||
href="/discord"
|
||||
target="_blank"
|
||||
class="flex items-center rounded-md w-full text-left py-4 px-4 hover:bg-gray-100 text-sm md:text-lg transition-all duration-200"
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
alt="discord logo"
|
||||
src="/discord-blue.png"
|
||||
class="w-6 h-5 mr-2 cursor-pointer"
|
||||
/>
|
||||
<span>Discord</span>
|
||||
</a>
|
||||
<a
|
||||
href="/github"
|
||||
target="_blank"
|
||||
class="flex items-center rounded-md w-full text-left py-4 px-4 hover:bg-gray-100 text-sm md:text-lg group transition-all duration-200"
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
alt="github logo"
|
||||
src="/github-mark.png"
|
||||
class="w-5 h-5 cursor-pointer transition-transform duration-200 group-hover:scale-110"
|
||||
/>
|
||||
<span class="ml-3 transition-colors duration-200 group-hover:text-black">Github</span>
|
||||
<span
|
||||
class="text-sm text-gray-600 font-medium ml-1 transition-colors duration-200 group-hover:text-black"
|
||||
>({stars})</span
|
||||
>
|
||||
</a>
|
||||
<a
|
||||
class="font-semibold after:content-['→'] rounded-md after:ml-2 w-full text-left py-4 px-4 hover:bg-gray-100 text-sm md:text-lg transition-all duration-200"
|
||||
href="https://app.classroomio.com"
|
||||
>
|
||||
Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.active {
|
||||
background-color: #f3f4f6;
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +0,0 @@
|
||||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let isOrg = false;
|
||||
export let className = '';
|
||||
|
||||
function handleClick() {
|
||||
if (!isOrg) {
|
||||
return goto('/');
|
||||
}
|
||||
|
||||
return goto('https://classroomio.com');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="{className} min-h-[80vh] flex items-center justify-center flex-col w-screen">
|
||||
<img src="https://brand.cdn.clsrio.com/404.png" alt="classroomio_error_image" />
|
||||
|
||||
<div class="flex flex-col items-center justify-center dark:text-white gap-3 w-4/5 lg:w-2/5">
|
||||
<p class="text-4xl dark:text-white font-semibold mb-5">
|
||||
{#if isOrg}
|
||||
Organization doesn't exist!
|
||||
{:else}
|
||||
Page not found
|
||||
{/if}
|
||||
</p>
|
||||
<p class=" text-gray-700 text-lg text-center dark:text-white mb-5">
|
||||
The page you are looking for doesn't exist or has been moved. Please go back to the homepage.
|
||||
</p>
|
||||
|
||||
<button
|
||||
class="text-base rounded-md bg-black transition-all delay-100 hover:shadow-xl hover:scale-95 px-6 py-4 font-semibold text-white lg:px-7 lg:py-3 lg:text-lg"
|
||||
on:click={handleClick}
|
||||
>
|
||||
Go Home
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,9 +0,0 @@
|
||||
<script>
|
||||
export let className = '';
|
||||
</script>
|
||||
|
||||
<header
|
||||
class="hero-bg lg:min-h-[20rem] mt-[10%] md:mt-16 pt-24 pb-16 w-full border-b border-slate-100 {className}"
|
||||
>
|
||||
<slot />
|
||||
</header>
|
||||
@@ -1,52 +0,0 @@
|
||||
<script>
|
||||
export let header = '';
|
||||
export let subText = '';
|
||||
export let btnLabel = '';
|
||||
export let link = '';
|
||||
export let demo = true;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="floating grad relative mx-auto my-[5%] flex h-[61vh] w-[100%] flex-col items-center justify-center px-[6%] py-[12%] text-center shadow-2xl lg:h-[54vh] lg:w-[70%] lg:rounded-3xl max-w-4xl"
|
||||
>
|
||||
<div>
|
||||
<h1
|
||||
class="font-display mt-3 text-3xl font-bold tracking-tight text-white lg:mt-2 lg:text-4xl max-w-2xl"
|
||||
>
|
||||
{header}
|
||||
</h1>
|
||||
<p class="mt-6 text-xl text-gray-300 lg:text-lg">
|
||||
{subText}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if demo}
|
||||
<button
|
||||
class="text-base mt-10 rounded-md bg-white px-6 py-4 font-semibold text-gray-900 lg:px-7 lg:py-3 lg:text-lg"
|
||||
data-cal-config="'layout':'month_view'"
|
||||
data-cal-link="classroomio/demo"
|
||||
>
|
||||
{btnLabel}
|
||||
</button>
|
||||
{:else}
|
||||
<a
|
||||
class="text-base mt-10 rounded-md bg-white px-6 py-4 font-semibold text-gray-900 lg:px-7 lg:py-3 lg:text-lg"
|
||||
href={link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
{btnLabel}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.grad {
|
||||
/* background: rgb(2, 51, 189);
|
||||
background: radial-gradient(circle at 50% 100%, rgba(2, 51, 189, 1) 1%, rgb(4, 2, 56) 100%); */
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-image: url('https://brand.cdn.clsrio.com/cta-bg.png');
|
||||
}
|
||||
</style>
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import Chip from '$lib/Chip/Chip.svelte';
|
||||
import { formatDate } from '$lib/utils/formatDate';
|
||||
import { Badge } from '@cio/ui/base/badge';
|
||||
import { formatDate } from '$lib/utils/format-date';
|
||||
|
||||
export let post;
|
||||
export let isRecommended = false;
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each post.tags as tag}
|
||||
<Chip label={tag} />
|
||||
<Badge variant="outline">{tag}</Badge>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Section from './CoreFeatureSection.svelte';
|
||||
import Section from './core-feature-section.svelte';
|
||||
import { PUBLIC_ENABLE_STATS } from '$env/static/public';
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<script>
|
||||
import { faqs } from '$lib/utils/faqs';
|
||||
import { PageSignupCTA } from '$lib/components';
|
||||
import * as Accordion from '@cio/ui/base/accordion';
|
||||
</script>
|
||||
|
||||
<div class="mb-20 mt-10 flex flex-col items-center justify-center">
|
||||
<header class="text-center">
|
||||
<h1 class="mt-2 text-xl md:text-4xl">
|
||||
Questions & Answers
|
||||
</h1>
|
||||
<p
|
||||
class="mt-4 text-sm md:text-lg font-normal tracking-normal ui:text-muted-foreground lg:px-[20%] lg:text-xl lg:tracking-wide"
|
||||
>
|
||||
Can't find the answer you're looking for?
|
||||
<a class="font-semibold text-blue-700" href="mailto:[email protected]" target="_blank"
|
||||
>Shoot us an email</a> and we'll get back to you ASAP.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="mt-[20%] lg:m-0 max-w-3xl w-full mx-auto px-2">
|
||||
<Accordion.Root type="single" class="w-full">
|
||||
{#each faqs as faq, index}
|
||||
<Accordion.Item value="faq-{index}">
|
||||
<Accordion.Trigger>{faq.question}</Accordion.Trigger>
|
||||
<Accordion.Content>
|
||||
{faq.answer}
|
||||
</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
{/each}
|
||||
</Accordion.Root>
|
||||
</div>
|
||||
|
||||
<PageSignupCTA
|
||||
header="Your Custom Academy – Up & Running in Minutes!"
|
||||
subText="Try before you buy. No credit card required."
|
||||
btnLabel="Book a demo"
|
||||
link="classroomio/demo"
|
||||
/>
|
||||
</div>
|
||||
@@ -0,0 +1,144 @@
|
||||
<script>
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import Facebook from '@lucide/svelte/icons/facebook';
|
||||
import Youtube from '@lucide/svelte/icons/youtube';
|
||||
</script>
|
||||
|
||||
<div class="border-t-[1px] px-[2%] lg:px-[14%]">
|
||||
<div class="flex flex-col items-center justify-between py-[10%] pl-5 lg:flex-row lg:pl-0">
|
||||
<div class="w-full min-w-[200px] lg:w-3/12">
|
||||
<img width="32" height="32" loading="lazy" src="/logo-192.png" alt="classroomio logo" class="mb-5" />
|
||||
<p class="mb-5 mr-2 text-sm leading-7 tracking-wide text-gray-600 lg:text-base lg:leading-7">
|
||||
The Open Source Learning Management System for Companies
|
||||
<br />
|
||||
ClassroomIO is the easiest way to grow your teaching business.
|
||||
</p>
|
||||
<!-- <a
|
||||
href="https://www.producthunt.com/posts/classroomio?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-classroomio"
|
||||
target="_blank"
|
||||
><img
|
||||
loading="lazy"
|
||||
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=425236&theme=light"
|
||||
alt="ClassroomIO - The Open Source Platform for Tech Bootcamps | Product Hunt"
|
||||
style="width: 250px; height: 54px;"
|
||||
width="250"
|
||||
height="54"
|
||||
/></a
|
||||
> -->
|
||||
<div class="mt-5 flex items-center gap-3 lg:mt-10 lg:gap-7">
|
||||
<Button href="https://www.facebook.com/classroomiohq" target="_blank" variant="ghost" aria-label="Facebook Link">
|
||||
<Facebook />
|
||||
</Button>
|
||||
<Button href="https://twitter.com/classroomio" target="_blank" variant="ghost" aria-label="Twitter Link">
|
||||
<img
|
||||
width="20"
|
||||
height="20"
|
||||
loading="lazy"
|
||||
src="/twitter.svg"
|
||||
alt=""
|
||||
/>
|
||||
</Button>
|
||||
<Button href="https://www.youtube.com/@ClassroomIO" target="_blank" variant="ghost" aria-label="Youtube Link">
|
||||
<Youtube />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav
|
||||
class="mt-16 flex w-full flex-col items-start justify-end gap-12 space-y-6 lg:mt-0 lg:w-3/4 lg:flex-row lg:space-y-0"
|
||||
>
|
||||
<div class="max-w-[120px]">
|
||||
<p class="mb-3 text-base font-semibold leading-6 text-gray-900 lg:mb-10 lg:text-base">Product</p>
|
||||
<ul class="space-y-2 lg:space-y-4">
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/#coursemanagement"> Course Management </a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/#customization"> Customizations </a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/#collaboration"> Forum </a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/#ai"> AI Lesson Planner </a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/#student-dashboard"> Student Dashboard </a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="max-w-[120px]">
|
||||
<p class="mb-3 text-base font-semibold leading-6 text-gray-900 lg:mb-10 lg:text-base">Solutions</p>
|
||||
<ul class="space-y-2 lg:space-y-4">
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/employee-training">Employee Training</a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/bootcamps">Bootcamps</a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/customer-education">Customer Education</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="max-w-[120px]">
|
||||
<p class="mb-3 text-base font-semibold leading-6 text-gray-900 lg:mb-10 lg:text-base">Resources</p>
|
||||
<ul class="space-y-2 lg:space-y-4">
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/contact">Contact us</a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/teach">Teach with us</a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a target="_blank" href="https://classroomio.com/docs/"> Help center </a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a target="_blank" href="https://classroomio.com/docs/quickstart/signup"> Getting Started </a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/oss-friends"> OSS Friends </a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="max-w-[120px]">
|
||||
<p class="mb-3 text-base font-semibold leading-6 text-gray-900 lg:mb-10 lg:text-base">Company</p>
|
||||
<ul class="space-y-2 lg:space-y-4">
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="/roadmap"> Roadmap </a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="mailto:[email protected]" target="_blank"> Support </a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="https://app.enzuzo.com/policies/privacy/958fc978-5477-11ee-a03b-7b111830c594" target="_blank">
|
||||
Privacy policy
|
||||
</a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a target="_blank" href="https://classroomio.com/docs/quickstart/signup"> Getting Started </a>
|
||||
</li>
|
||||
<li class="text-sm leading-6 tracking-wide text-gray-600 hover:text-gray-900 lg:text-base">
|
||||
<a href="https://app.enzuzo.com/policies/tos/958fc978-5477-11ee-a03b-7b111830c594" target="_blank">
|
||||
Terms of use
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<footer
|
||||
class="border-t py-3 text-center text-xs font-light leading-5 text-gray-500 lg:text-left lg:text-base"
|
||||
>
|
||||
<p>All rights reserved.</p>
|
||||
<p class="flex items-center justify-center">
|
||||
ClassroomIO © {new Date().getFullYear()} by
|
||||
<a href="https://twitter.com/rotimi_best" target="_blank" class="mx-1 text-blue-700 underline">Rotimi Best</a>
|
||||
from
|
||||
<img loading="lazy" alt="Nigerian Flag" src="/ng.png" class="ml-2 h-3 w-3" />
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
+30
-39
@@ -3,6 +3,7 @@
|
||||
import { fly } from 'svelte/transition';
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
import { PUBLIC_ENABLE_USERS_COMPANIES } from '$env/static/public';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
|
||||
const names = [
|
||||
{ label: 'Audience', color: '#3ADFEC' },
|
||||
@@ -30,32 +31,32 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="hero-bg relative border-b-2 text-center bg-[#F5F8FE] h-full mt-[5%] w-full">
|
||||
<div class="pt-24 w-full">
|
||||
<div class="mb-10 ml-[5%] lg:ml-0 flex w-full items-center justify-start lg:justify-center">
|
||||
<div class="hero-bg pt-18 relative h-full w-full border-b-2 bg-[#F5F8FE] text-center">
|
||||
<div class="w-full pt-24">
|
||||
<div class="mb-10 ml-[5%] flex w-full items-center justify-start lg:ml-0 lg:justify-center">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
href="/github"
|
||||
style="color: rgb(75, 85, 99);"
|
||||
class="rounded-full border-2 border-[#C2D2FF] px-4 py-1 text-sm font-medium bg-[#DCE5FF] text-[#4B5563] hover:underline"
|
||||
>We are <span class="text-blue-700">100%</span> open source</a
|
||||
class="rounded-full border-2 border-[#C2D2FF] bg-[#DCE5FF] px-4 py-1 text-sm font-medium text-[#4B5563] hover:underline"
|
||||
>We are <span class="ui:text-primary">100%</span> open source</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="justify-between w-[90%] absolute top-[10%] left-[5%] hidden lg:flex">
|
||||
<div class="absolute left-[5%] top-[10%] hidden w-[90%] justify-between lg:flex">
|
||||
{#if animate}
|
||||
<div
|
||||
class="relative transition-all hover:scale-110 rotate-12"
|
||||
class="relative rotate-12 transition-all hover:scale-110"
|
||||
transition:fly={{ y: 100, delay: 0, easing: sineInOut }}
|
||||
>
|
||||
<a
|
||||
href="/#collaboration"
|
||||
class="px-6 py-2 border-[1.5px] border-[#3ADFEC] rounded-[4px] bg-white text-[#3ADFEC] text-lg font-bold"
|
||||
class="rounded-[4px] border-[1.5px] border-[#3ADFEC] bg-white px-6 py-2 text-lg font-bold text-[#3ADFEC]"
|
||||
>Collaboration</a
|
||||
>
|
||||
<svg
|
||||
class="absolute -right-1 -bottom-2.5"
|
||||
class="absolute -bottom-2.5 -right-1"
|
||||
width="11"
|
||||
height="11"
|
||||
viewBox="0 0 11 11"
|
||||
@@ -67,17 +68,17 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="relative transition-all hover:scale-110 -rotate-12"
|
||||
class="relative -rotate-12 transition-all hover:scale-110"
|
||||
transition:fly={{ x: 100, delay: 50, easing: sineInOut }}
|
||||
>
|
||||
<a
|
||||
href="/github"
|
||||
target="_blank"
|
||||
class="px-6 py-2 border-[1.5px] border-[#BF0696] rounded-[4px] bg-white text-[#BF0696] text-lg font-bold"
|
||||
class="rounded-[4px] border-[1.5px] border-[#BF0696] bg-white px-6 py-2 text-lg font-bold text-[#BF0696]"
|
||||
>Open Source</a
|
||||
>
|
||||
<svg
|
||||
class="absolute -right-1 -bottom-2.5"
|
||||
class="absolute -bottom-2.5 -right-1"
|
||||
width="11"
|
||||
height="11"
|
||||
viewBox="0 0 11 11"
|
||||
@@ -91,21 +92,19 @@
|
||||
</div>
|
||||
|
||||
<div class="w-full">
|
||||
<div
|
||||
class="font-bold md:leading-[1.5] text-left lg:text-center ml-6 text-slate-900 text-4xl lg:text-7xl"
|
||||
>
|
||||
<div class="ml-6 text-left text-2xl font-semibold text-slate-900 lg:text-center lg:text-6xl">
|
||||
<span>Educate Your</span>
|
||||
<div class="flex-wrap flex items-center justify-center lg:gap-3 mb-2">
|
||||
<div class="mb-2 flex flex-wrap items-center justify-center lg:gap-3">
|
||||
<span
|
||||
class="w-full lg:w-[48%] h-[40px] md:h-[50px] lg:h-[75px] mb-0 mt-1 md:mt-0 lg:-mb-4 relative inline-block overflow-hidden lg:text-center"
|
||||
class="relative mb-0 mt-1 inline-block h-[40px] w-full overflow-hidden md:mt-0 md:h-[50px] lg:-mb-4 lg:h-[75px] lg:w-[48%] lg:text-center"
|
||||
>
|
||||
{#key titleIndex}
|
||||
<span
|
||||
class="absolute left-0 right-[unset] lg:right-0 lg:left-[unset] w-full"
|
||||
class="absolute left-0 right-[unset] w-full lg:left-[unset] lg:right-0"
|
||||
style="color: {names[titleIndex].color}"
|
||||
transition:fly={{ y: 100, delay: 0, easing: sineInOut }}
|
||||
>
|
||||
<span class="w-full text-center m-auto">
|
||||
<span class="m-auto w-full text-center">
|
||||
{names[titleIndex].label}
|
||||
</span>
|
||||
</span>
|
||||
@@ -115,27 +114,27 @@
|
||||
<span>from one Platform</span>
|
||||
</div>
|
||||
|
||||
<p class="text-base mx-auto mt-10 px-4 text-slate-700 md:text-lg lg:mt-6 lg:w-[42%] lg:p-0">
|
||||
<p class="mx-auto mt-10 px-4 ui:text-muted-foreground md:text-lg lg:mt-6 lg:w-[42%] lg:p-0">
|
||||
ClassroomIO is an open source learning management system for companies. We give you a suite
|
||||
of tools to help you achieve your training, onboarding and certification goals.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="justify-between w-[90%] absolute top-[30%] left-[5%] hidden lg:flex">
|
||||
<div class="absolute left-[5%] top-[30%] hidden w-[90%] justify-between lg:flex">
|
||||
{#if animate}
|
||||
<div
|
||||
class="relative transition-all hover:scale-110 -rotate-12"
|
||||
class="relative -rotate-12 transition-all hover:scale-110"
|
||||
transition:fly={{ x: -100, delay: 100, easing: sineInOut }}
|
||||
>
|
||||
<a
|
||||
href="/#customization"
|
||||
class="px-6 py-2 border-[1.5px] border-[#0233BD] rounded-[4px] bg-white text-[#0233BD] text-lg font-bold"
|
||||
class="rounded-[4px] border-[1.5px] border-[#0233BD] bg-white px-6 py-2 text-lg font-bold text-[#0233BD]"
|
||||
>
|
||||
Customizable LMS
|
||||
</a>
|
||||
|
||||
<svg
|
||||
class="absolute -right-1 -bottom-2.5"
|
||||
class="absolute -bottom-2.5 -right-1"
|
||||
width="11"
|
||||
height="11"
|
||||
viewBox="0 0 11 11"
|
||||
@@ -146,17 +145,17 @@
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="relative transition-all hover:scale-110 rotate-12"
|
||||
class="relative rotate-12 transition-all hover:scale-110"
|
||||
transition:fly={{ y: 100, delay: 150, easing: sineInOut }}
|
||||
>
|
||||
<a
|
||||
href="/#ai"
|
||||
class="px-6 py-2 border-[1.5px] border-[#9747FF] rounded-[4px] bg-white text-[#9747FF] text-lg font-bold"
|
||||
class="rounded-[4px] border-[1.5px] border-[#9747FF] bg-white px-6 py-2 text-lg font-bold text-[#9747FF]"
|
||||
>AI Support</a
|
||||
>
|
||||
|
||||
<svg
|
||||
class="absolute -right-1 -bottom-2.5"
|
||||
class="absolute -bottom-2.5 -right-1"
|
||||
width="11"
|
||||
height="11"
|
||||
viewBox="0 0 11 11"
|
||||
@@ -170,20 +169,12 @@
|
||||
</div>
|
||||
|
||||
<div class="my-10 flex flex-col items-center justify-center gap-4 md:flex-row">
|
||||
<a
|
||||
class="md:text-base w-11/12 rounded-md border-gray-200 border-2 px-6 py-3 text-sm font-medium text-center transition-all delay-100 hover:bg-gray-200 hover:shadow-xl hover:scale-95 md:w-fit lg:px-6 lg:py-4 lg:font-semibold"
|
||||
href="/demo"
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
<Button variant="outline" href="/demo" target="_blank" rel="noopener noreferrer nofollow">
|
||||
Book a demo
|
||||
</a>
|
||||
<a
|
||||
class="md:text-base w-11/12 rounded-md bg-blue-700 px-6 py-3 text-sm font-medium text-white text-center transition-all delay-100 hover:bg-blue-800 hover:shadow-xl hover:scale-95 md:w-fit lg:px-6 lg:py-4 lg:font-semibold"
|
||||
href="/signup"
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
</Button>
|
||||
<Button href="/signup" target="_blank" rel="noopener noreferrer nofollow">
|
||||
Sign Up for Free
|
||||
</a>
|
||||
</Button>
|
||||
<!-- <a
|
||||
class="font-medium lg:font-semibold text-sm md:text-base w-11/12 md:w-fit border px-6 py-3 lg:px-10 lg:py-5 rounded-md"
|
||||
href="/github"
|
||||
@@ -0,0 +1,22 @@
|
||||
// UI Components - Named exports for easy importing
|
||||
export { default as BlogListItem } from './blog-list-item.svelte';
|
||||
export { default as CoreFeatureSection } from './core-feature-section.svelte';
|
||||
export { default as CoreFeatures } from './core-features.svelte';
|
||||
export { default as CustomImg } from './custom-img.svelte';
|
||||
export { default as Faq } from './faq.svelte';
|
||||
export { default as Footer } from './footer.svelte';
|
||||
export { default as Hero } from './hero.svelte';
|
||||
export { default as Modal } from './modal.svelte';
|
||||
export { default as MoreFeatures } from './more-features.svelte';
|
||||
export { default as Navigation } from './navigation.svelte';
|
||||
export { default as NotFound } from './not-found.svelte';
|
||||
export { default as PageHeader } from './page-header.svelte';
|
||||
export { default as PageSignupCTA } from './page-signup-cta.svelte';
|
||||
export { default as SenjaEmbed } from './senja-embed.svelte';
|
||||
export { default as StudentDashboard } from './student-dashboard.svelte';
|
||||
export { default as Success } from './success.svelte';
|
||||
export { default as Testimonial } from './testimonial.svelte';
|
||||
export { default as Timeline } from './timeline.svelte';
|
||||
export { default as ToolsHeader } from './tools-header.svelte';
|
||||
export { default as UploadWidget } from './upload-widget.svelte';
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
<script lang="ts">
|
||||
import ChevronDown from '@lucide/svelte/icons/chevron-down';
|
||||
import Gamepad from '@lucide/svelte/icons/gamepad';
|
||||
import Hourglass from '@lucide/svelte/icons/hourglass';
|
||||
import LoaderCircle from '@lucide/svelte/icons/loader-circle';
|
||||
import LocateFixed from '@lucide/svelte/icons/locate-fixed';
|
||||
import Menu from '@lucide/svelte/icons/menu';
|
||||
import MessagesSquare from '@lucide/svelte/icons/messages-square';
|
||||
import MousePointerClick from '@lucide/svelte/icons/mouse-pointer-click';
|
||||
import Timer from '@lucide/svelte/icons/timer';
|
||||
import X from '@lucide/svelte/icons/x';
|
||||
import { page } from '$app/state';
|
||||
import { fly } from 'svelte/transition';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import * as NavigationMenu from '@cio/ui/base/navigation-menu';
|
||||
import LibraryBigIcon from '@lucide/svelte/icons/library-big';
|
||||
|
||||
|
||||
export let stars: number;
|
||||
|
||||
let showDrawer = false;
|
||||
let showSolutions = false;
|
||||
let activeLink = '';
|
||||
|
||||
function handleShowDrawer() {
|
||||
showDrawer = !showDrawer;
|
||||
}
|
||||
|
||||
function handleShowSolutions() {
|
||||
showSolutions = !showSolutions;
|
||||
}
|
||||
|
||||
const solutions = [
|
||||
{
|
||||
key: 'employee-training',
|
||||
title: 'Employee Training',
|
||||
subtitle: 'Keep your team in sync.'
|
||||
},
|
||||
{
|
||||
key: 'bootcamps',
|
||||
title: 'Bootcamps',
|
||||
subtitle: 'Drive student satisfaction.'
|
||||
},
|
||||
{
|
||||
key: 'customer-education',
|
||||
title: 'Customer Education',
|
||||
subtitle: 'Teach customers your product.'
|
||||
}
|
||||
];
|
||||
|
||||
const freeTools = [
|
||||
{
|
||||
key: 'progress',
|
||||
title: 'Progress Tracker',
|
||||
subtitle: 'Monitor learning journeys.'
|
||||
},
|
||||
{
|
||||
key: 'pomodoro',
|
||||
title: 'Pomodoro Timer',
|
||||
subtitle: 'Boost focus and productivity.'
|
||||
},
|
||||
{
|
||||
key: 'name-picker',
|
||||
title: 'Name Picker',
|
||||
subtitle: 'Randomly select names.'
|
||||
},
|
||||
{
|
||||
key: 'stopwatch',
|
||||
title: 'Activity Stopwatch',
|
||||
subtitle: 'Track time accurately.'
|
||||
},
|
||||
{
|
||||
key: 'tic-tac-toe',
|
||||
title: 'Tic Tac Toe',
|
||||
subtitle: 'Play the classic game.'
|
||||
}
|
||||
];
|
||||
|
||||
type ListItemProps = HTMLAttributes<HTMLAnchorElement> & {
|
||||
title: string;
|
||||
href: string;
|
||||
subtitle: string;
|
||||
key: string;
|
||||
};
|
||||
|
||||
$: activeLink = page.url.pathname;
|
||||
$: activeHash = page.url.hash;
|
||||
$: isSolutionsActive = solutions.some((s) => activeHash.includes(s.key));
|
||||
</script>
|
||||
|
||||
{#snippet list_item({ title, subtitle, href, key, class: className, ...restProps }: ListItemProps)}
|
||||
<li>
|
||||
<NavigationMenu.Link>
|
||||
{#snippet child()}
|
||||
<a
|
||||
{href}
|
||||
class="flex w-full flex-row items-center rounded-lg p-2 no-underline transition-colors duration-200 hover:bg-gray-100 focus:bg-gray-100 focus:outline-none {className} [&_svg]:stroke-blue-600"
|
||||
{...restProps}
|
||||
>
|
||||
<div class="shrink-0">
|
||||
{#if key === 'employee-training'}
|
||||
<LibraryBigIcon size={24} />
|
||||
{:else if key === 'bootcamps'}
|
||||
<LocateFixed size={24} />
|
||||
{:else if key === 'customer-education'}
|
||||
<MessagesSquare size={24} />
|
||||
{:else if key === 'progress'}
|
||||
<LoaderCircle size={24} />
|
||||
{:else if key === 'pomodoro'}
|
||||
<Hourglass size={24} />
|
||||
{:else if key === 'name-picker'}
|
||||
<MousePointerClick size={24} />
|
||||
{:else if key === 'stopwatch'}
|
||||
<Timer size={24} />
|
||||
{:else if key === 'tic-tac-toe'}
|
||||
<Gamepad size={24} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="ml-3 text-start">
|
||||
<p class="text-sm">
|
||||
{title}
|
||||
</p>
|
||||
<p class="mt-1 text-xs ui:text-muted-foreground">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
{/snippet}
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
{/snippet}
|
||||
|
||||
<section class="z-50 fixed top-0 w-full bg-white border-b">
|
||||
<div class="mx-auto flex w-full items-center justify-between gap-20 py-1 lg:w-[90%] lg:px-0 px-4">
|
||||
<a href="/" class="w-[10%]">
|
||||
<div class="flex w-full items-center">
|
||||
<img loading="lazy" width="28" height="28" src="/logo-512.png" alt="classroomio logo" class="w-6 md:w-7" />
|
||||
<h1 class="ml-2 text-xs font-medium md:text-base">ClassroomIO</h1>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<section class="hidden lg:block">
|
||||
<NavigationMenu.Root class="rounded-lg bg-white p-1">
|
||||
<NavigationMenu.List class="flex w-full items-center justify-center gap-1">
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Trigger
|
||||
class="flex cursor-pointer items-center rounded-md px-4 py-2 text-sm font-medium text-gray-800 transition-all duration-200 hover:bg-gray-100 data-[state=open]:bg-gray-100"
|
||||
>
|
||||
<a href="/" class="no-underline"> Solutions </a>
|
||||
</NavigationMenu.Trigger>
|
||||
<NavigationMenu.Content>
|
||||
<ul class="grid w-[560px] grid-cols-2 gap-2 p-2">
|
||||
{#each solutions as solution}
|
||||
{@render list_item({
|
||||
href: `/${solution.key}`,
|
||||
title: solution.title,
|
||||
subtitle: solution.subtitle,
|
||||
key: solution.key
|
||||
})}
|
||||
{/each}
|
||||
</ul>
|
||||
</NavigationMenu.Content>
|
||||
</NavigationMenu.Item>
|
||||
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Trigger
|
||||
class="flex cursor-pointer items-center rounded-md px-4 py-2 text-sm font-medium text-gray-800 transition-all duration-200 hover:bg-gray-100 data-[state=open]:bg-gray-100"
|
||||
>
|
||||
<a href="/tools" class="no-underline"> Free Tools </a>
|
||||
</NavigationMenu.Trigger>
|
||||
<NavigationMenu.Content>
|
||||
<ul class="grid w-[560px] grid-cols-2 gap-2 p-2">
|
||||
{#each freeTools as tool}
|
||||
{@render list_item({
|
||||
href: `/tools/${tool.key}`,
|
||||
title: tool.title,
|
||||
subtitle: tool.subtitle,
|
||||
key: tool.key
|
||||
})}
|
||||
{/each}
|
||||
</ul>
|
||||
</NavigationMenu.Content>
|
||||
</NavigationMenu.Item>
|
||||
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Link>
|
||||
{#snippet child()}
|
||||
<a
|
||||
href="/blog"
|
||||
class="cursor-pointer rounded-md px-4 py-2 text-sm font-medium text-gray-800 no-underline transition-all duration-200 hover:bg-gray-100 {activeLink.startsWith(
|
||||
'/blog'
|
||||
)
|
||||
? 'bg-gray-100'
|
||||
: ''}"
|
||||
>
|
||||
Blog
|
||||
</a>
|
||||
{/snippet}
|
||||
</NavigationMenu.Link>
|
||||
</NavigationMenu.Item>
|
||||
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Link>
|
||||
{#snippet child()}
|
||||
<a
|
||||
href="/pricing"
|
||||
class="cursor-pointer rounded-md px-4 py-2 text-sm font-medium text-gray-800 no-underline transition-all duration-200 hover:bg-gray-100 {activeLink.startsWith(
|
||||
'/pricing'
|
||||
)
|
||||
? 'bg-gray-100'
|
||||
: ''}"
|
||||
>
|
||||
Pricing
|
||||
</a>
|
||||
{/snippet}
|
||||
</NavigationMenu.Link>
|
||||
</NavigationMenu.Item>
|
||||
</NavigationMenu.List>
|
||||
</NavigationMenu.Root>
|
||||
</section>
|
||||
|
||||
<div class="hidden flex-row items-center justify-between gap-3 md:hidden lg:flex">
|
||||
<a href="/discord" target="_blank" class="flex items-center transition-opacity duration-200 hover:opacity-80">
|
||||
<img loading="lazy" alt="discord logo" src="/discord-blue.png" class="h-5 w-6 cursor-pointer" />
|
||||
</a>
|
||||
<div class="flex items-center">
|
||||
<a
|
||||
href="/github"
|
||||
target="_blank"
|
||||
class="group flex items-center gap-1.5 rounded-md p-2 transition-all duration-200 ease-in-out hover:bg-gray-100"
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
alt="github logo"
|
||||
src="/github-mark.png"
|
||||
class="h-5 w-5 cursor-pointer transition-transform duration-200 group-hover:scale-110"
|
||||
/>
|
||||
<span
|
||||
class="text-sm font-medium leading-none text-gray-600 transition-colors duration-200 group-hover:text-black"
|
||||
>
|
||||
{stars}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
class="text-sm font-medium transition-opacity duration-200 after:ml-2 after:content-['→'] hover:opacity-80"
|
||||
href="https://app.classroomio.com"
|
||||
>
|
||||
Dashboard
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button type="button" aria-label="Hamburger Menu" class="block md:block lg:hidden" on:click={handleShowSolutions}>
|
||||
<Menu size={24} />
|
||||
</button>
|
||||
|
||||
{#if showSolutions}
|
||||
<div
|
||||
in:fly={{ x: 20, duration: 700 }}
|
||||
out:fly={{ x: 20, duration: 400 }}
|
||||
class="absolute right-0 top-0 h-screen w-[60%] border-2 bg-white px-3 pb-2 pt-3 sm:block md:w-[40%] md:p-7 lg:hidden"
|
||||
>
|
||||
<div class="mb-5 flex justify-between py-2">
|
||||
<img loading="lazy" width="20" height="20" src="/logo-512.png" alt="classroomio logo" class="w-[15%]" />
|
||||
<button class="mr-5" on:click={handleShowSolutions}>
|
||||
<X size={24} />
|
||||
</button>
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="flex w-full flex-col items-center justify-between lg:flex-row">
|
||||
<li class="w-full cursor-pointer text-sm font-semibold text-gray-800 md:text-lg">
|
||||
<button
|
||||
class="flex w-full items-center justify-between rounded-lg px-4 py-3 transition-all duration-200 hover:bg-gray-100"
|
||||
on:click={handleShowDrawer}
|
||||
class:active={isSolutionsActive}
|
||||
>
|
||||
Our Superpowers <ChevronDown />
|
||||
</button>
|
||||
{#if showDrawer}
|
||||
<div in:fly={{ y: -20, duration: 700 }} out:fly={{ y: 20, duration: 400 }}>
|
||||
{#each solutions as solution}
|
||||
<a
|
||||
href="/{solution.key}"
|
||||
on:click={() => {
|
||||
handleShowSolutions();
|
||||
}}
|
||||
>
|
||||
<p
|
||||
class="rounded-lg py-2.5 pl-5 text-xs font-normal text-gray-700 transition-colors duration-200 hover:bg-gray-100"
|
||||
>
|
||||
{solution.title}
|
||||
</p>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</li>
|
||||
<a
|
||||
class="w-full cursor-pointer rounded-xl px-4 py-3 text-sm font-semibold text-gray-800 transition-all duration-200 hover:bg-gray-100 md:text-lg"
|
||||
on:click={handleShowSolutions}
|
||||
href="/tools"
|
||||
>
|
||||
<li>Free Tools</li>
|
||||
</a>
|
||||
<a
|
||||
class="w-full cursor-pointer rounded-md px-4 py-3 text-sm font-semibold text-gray-800 transition-all duration-200 hover:bg-gray-100 md:text-lg"
|
||||
on:click={() => {
|
||||
handleShowSolutions();
|
||||
}}
|
||||
class:active={activeLink.startsWith('/blog')}
|
||||
href="/blog"
|
||||
>
|
||||
<li>Blog</li>
|
||||
</a>
|
||||
<a
|
||||
class="w-full cursor-pointer rounded-xl px-4 py-3 text-sm font-semibold text-gray-800 transition-all duration-200 hover:bg-gray-100 md:text-lg"
|
||||
on:click={() => {
|
||||
handleShowSolutions();
|
||||
}}
|
||||
href="/pricing"
|
||||
class:active={activeLink.startsWith('/pricing')}
|
||||
>
|
||||
<li>Pricing</li>
|
||||
</a>
|
||||
<a
|
||||
class="w-full cursor-pointer rounded-md px-4 py-3 text-sm font-semibold text-gray-800 transition-all duration-200 hover:bg-gray-100 md:text-lg"
|
||||
on:click={() => {
|
||||
handleShowSolutions();
|
||||
}}
|
||||
class:active={activeHash.includes('morefeatures')}
|
||||
href="/#morefeatures"
|
||||
>
|
||||
<li>More features</li>
|
||||
</a>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="mt-5 flex flex-col items-start gap-y-2 border-t pt-5">
|
||||
<a
|
||||
href="/discord"
|
||||
target="_blank"
|
||||
class="flex w-full items-center rounded-md px-4 py-4 text-left text-sm transition-all duration-200 hover:bg-gray-100 md:text-lg"
|
||||
>
|
||||
<img loading="lazy" alt="discord logo" src="/discord-blue.png" class="mr-2 h-5 w-6 cursor-pointer" />
|
||||
<span>Discord</span>
|
||||
</a>
|
||||
<a
|
||||
href="/github"
|
||||
target="_blank"
|
||||
class="group flex w-full items-center rounded-md px-4 py-4 text-left text-sm transition-all duration-200 hover:bg-gray-100 md:text-lg"
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
alt="github logo"
|
||||
src="/github-mark.png"
|
||||
class="h-5 w-5 cursor-pointer transition-transform duration-200 group-hover:scale-110"
|
||||
/>
|
||||
<span class="ml-3 transition-colors duration-200 group-hover:text-black">Github</span>
|
||||
<span class="ml-1 text-sm font-medium text-gray-600 transition-colors duration-200 group-hover:text-black"
|
||||
>({stars})</span
|
||||
>
|
||||
</a>
|
||||
<a
|
||||
class="w-full rounded-md px-4 py-4 text-left text-sm font-semibold transition-all duration-200 after:ml-2 after:content-['→'] hover:bg-gray-100 md:text-lg"
|
||||
href="https://app.classroomio.com"
|
||||
>
|
||||
Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.active {
|
||||
background-color: #f3f4f6;
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
import { Empty } from '@cio/ui/custom/empty';
|
||||
import SearchXIcon from '@lucide/svelte/icons/search-x';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
|
||||
export let isOrg = false;
|
||||
export let className = '';
|
||||
|
||||
function handleClick() {
|
||||
if (!isOrg) {
|
||||
return goto('/');
|
||||
}
|
||||
|
||||
return goto('https://classroomio.com');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="{className} min-h-[80vh] w-screen flex items-center justify-center">
|
||||
|
||||
<Empty
|
||||
title={isOrg ? 'Organization doesn\'t exist!' : 'Page not found'}
|
||||
description="The page you are looking for doesn't exist or has been moved. Please go back to the homepage."
|
||||
icon={SearchXIcon}
|
||||
variant="page"
|
||||
>
|
||||
<Button
|
||||
onclick={handleClick}
|
||||
>
|
||||
Go Home
|
||||
</Button>
|
||||
</Empty>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script>
|
||||
export let className = '';
|
||||
</script>
|
||||
|
||||
<header class="hero-bg w-full border-b border-slate-100 pb-16 pt-32 lg:min-h-[20rem] {className}">
|
||||
<slot />
|
||||
</header>
|
||||
@@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
|
||||
interface Props {
|
||||
header?: string;
|
||||
subText?: string;
|
||||
btnLabel?: string;
|
||||
link?: string;
|
||||
demo?: boolean;
|
||||
}
|
||||
|
||||
let { header = '', subText = '', btnLabel = '', link = '', demo = true }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="floating grad relative mx-auto my-5 flex h-[61vh] w-full flex-col items-center justify-center px-6 py-12 text-center shadow-2xl lg:h-[50vh] lg:w-[70%] lg:rounded-3xl max-w-4xl space-y-8"
|
||||
>
|
||||
<div>
|
||||
<h3
|
||||
class="font-display mt-3 text-xl font-semibold tracking-tight text-white lg:mt-2 lg:text-4xl max-w-2xl"
|
||||
>
|
||||
{header}
|
||||
</h3>
|
||||
<p class="mt-6 text-xl text-gray-300 lg:text-lg">
|
||||
{subText}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if demo}
|
||||
<Button
|
||||
variant="ghost"
|
||||
data-cal-config="'layout':'month_view'"
|
||||
data-cal-link="classroomio/demo"
|
||||
class="bg-white!"
|
||||
>
|
||||
{btnLabel}
|
||||
</Button>
|
||||
{:else}
|
||||
<Button
|
||||
href={link}
|
||||
variant="link"
|
||||
target="_blank"
|
||||
class="ui:bg-secondary ui:text-secondary-foreground"
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
{btnLabel}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.grad {
|
||||
/* background: rgb(2, 51, 189);
|
||||
background: radial-gradient(circle at 50% 100%, rgba(2, 51, 189, 1) 1%, rgb(4, 2, 56) 100%); */
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-image: url('https://brand.cdn.clsrio.com/cta-bg.png');
|
||||
}
|
||||
</style>
|
||||
+5
-7
@@ -1,11 +1,9 @@
|
||||
<script>
|
||||
import { Badge } from '@cio/ui/base/badge';
|
||||
</script>
|
||||
|
||||
<section id="student-dashboard" class="text-center text-white py-[10%] px-3 lg:px-0 bg-black">
|
||||
<div class="mb-10">
|
||||
<a
|
||||
href="#student-dashboard"
|
||||
class="rounded-full px-4 py-2 text-sm font-medium hover:underline bg-blue-700 underline"
|
||||
><span class="">yourorg</span>.classroomio.com</a
|
||||
>
|
||||
</div>
|
||||
<Badge class="py-2! px-4! underline text-sm!">yourorg.classroomio.com</Badge>
|
||||
<h1
|
||||
class="mx-auto text-3xl md:text-4xl lg:text-6xl font-normal leading-[1.5] flex flex-col items-center gap-4"
|
||||
>
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import Button from '$lib/Button/Button.svelte';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -13,5 +13,5 @@
|
||||
We'll get back to you as soon as possible. In the meantime, Explore and enjoy features on our
|
||||
website
|
||||
</p>
|
||||
<Button label="Go to Home" className="px-10 md:px-20" onClick={() => goto('/')} />
|
||||
<Button class="px-10 md:px-20" onclick={() => goto('/')}>Go to Home</Button>
|
||||
</div>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script>
|
||||
import { SenjaEmbed } from '$lib/components';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
</script>
|
||||
|
||||
<section id="testimonial" class="flex flex-col items-center py-[5%] px-3 lg:px-0 space-y-2">
|
||||
<h1
|
||||
class="mx-auto text-3xl md:text-4xl lg:text-5xl font-normal leading-normal flex flex-col items-center gap-4 my-0"
|
||||
>
|
||||
Hear from Happy ClassroomIO Users
|
||||
</h1>
|
||||
|
||||
<p
|
||||
class="mx-auto text-center px-4 lg:p-0 text-base tracking-wide leading-6 ui:text-muted-foreground lg:w-2/4"
|
||||
>
|
||||
See what excites them about building and managing bootcamps with ClassroomIO.
|
||||
</p>
|
||||
<Button
|
||||
href="/signup"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
Sign Up for Free
|
||||
</Button>
|
||||
|
||||
<div class="relative my-5 md:my-10 w-[85%] lg:w-[70%]">
|
||||
<SenjaEmbed id="3e847d7a-13d3-483e-8a71-5b35273dcc6c" />
|
||||
</div>
|
||||
</section>
|
||||
+1
-1
@@ -2,6 +2,6 @@
|
||||
export let className = '';
|
||||
</script>
|
||||
|
||||
<header class="tools-hero-bg text-center py-14 {className} border-b border-slate-100 bg-white">
|
||||
<header class="tools-hero-bg py-24 text-center {className} border-b border-slate-100 bg-white">
|
||||
<slot />
|
||||
</header>
|
||||
+8
-10
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import ImageCopy from 'carbon-icons-svelte/lib/ImageCopy.svelte';
|
||||
import TrashCan from 'carbon-icons-svelte/lib/TrashCan.svelte';
|
||||
import Copy from '@lucide/svelte/icons/copy';
|
||||
import Trash2 from '@lucide/svelte/icons/trash-2';
|
||||
|
||||
export let imageURL = '';
|
||||
export let label = '';
|
||||
@@ -35,7 +35,7 @@
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div>
|
||||
{#if label}
|
||||
<p class="dark:text-white text-left w-full flex items-center justify-between {labelClassName}">
|
||||
<p class="flex w-full items-center justify-between text-left dark:text-white {labelClassName}">
|
||||
<span>
|
||||
{label}
|
||||
{#if isRequired}
|
||||
@@ -45,13 +45,13 @@
|
||||
|
||||
{#if imageURL}
|
||||
<span title="clear image" on:click={deleteImage}>
|
||||
<TrashCan class="fill-red-500 " />
|
||||
<Trash2 class="fill-red-500 " />
|
||||
</span>
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
<div
|
||||
class="flex items-center justify-center w-full border rounded-md min-h-[200px] p-4"
|
||||
class="flex min-h-[200px] w-full items-center justify-center rounded-md border p-4"
|
||||
on:click={() => fileInput.click()}
|
||||
>
|
||||
<input
|
||||
@@ -63,13 +63,11 @@
|
||||
required={isRequired}
|
||||
/>
|
||||
{#if imageURL}
|
||||
<img src={imageURL} alt="selected file" class="w-[30%] h-auto mt-4" />
|
||||
<img src={imageURL} alt="selected file" class="mt-4 h-auto w-[30%]" />
|
||||
{:else}
|
||||
<div class="space-y-4">
|
||||
<ImageCopy size={24} class="mx-auto" />
|
||||
<p class="text-center text-sm text-gray-500 my-2">
|
||||
Max file size: 10MB, accepted: jpeg, jpg, png, gif
|
||||
</p>
|
||||
<Copy size={24} class="mx-auto" />
|
||||
<p class="my-2 text-center text-sm text-gray-500">Max file size: 10MB, accepted: jpeg, jpg, png, gif</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,2 +0,0 @@
|
||||
import img from './img.svelte';
|
||||
export { img };
|
||||
@@ -0,0 +1,87 @@
|
||||
<script>
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import * as Field from '@cio/ui/base/field';
|
||||
import { Input } from '@cio/ui/base/input';
|
||||
import { Textarea } from '@cio/ui/base/textarea';
|
||||
import { isFormValid } from '$lib/utils/is-form-valid';
|
||||
import { submitForm } from '$lib/utils/submit-form';
|
||||
import { Success } from '$lib/components';
|
||||
|
||||
let name = '';
|
||||
let email = '';
|
||||
let title = '';
|
||||
let description = '';
|
||||
|
||||
let isLoading = false;
|
||||
let errorMessage = '';
|
||||
let sent = false;
|
||||
|
||||
const handleClick = async () => {
|
||||
const data = { name, email, title, description };
|
||||
|
||||
if (!isFormValid(data)) {
|
||||
errorMessage = 'Please fill out all required fields.';
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading = true;
|
||||
|
||||
const formData = {
|
||||
title: `[FEEDBACK] ${title}`,
|
||||
description: `My name is ${JSON.stringify(name)}, email is ${JSON.stringify(
|
||||
email
|
||||
)}. \n\n${JSON.stringify(description)}.`,
|
||||
replyTo: email
|
||||
};
|
||||
|
||||
try {
|
||||
await submitForm(formData);
|
||||
sent = true;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if sent}
|
||||
<Success />
|
||||
{:else}
|
||||
<div class="w-full my-10 space-y-5">
|
||||
<Field.Group>
|
||||
<div class="flex flex-col md:flex-row gap-10">
|
||||
<Field.Field class="w-full">
|
||||
<Field.Label for="feedback-name">Your Name</Field.Label>
|
||||
<Input id="feedback-name" bind:value={name} required />
|
||||
</Field.Field>
|
||||
<Field.Field class="w-full">
|
||||
<Field.Label for="feedback-email">Your Email</Field.Label>
|
||||
<Input id="feedback-email" type="email" bind:value={email} required />
|
||||
</Field.Field>
|
||||
</div>
|
||||
<Field.Field>
|
||||
<Field.Label for="feedback-subject">Subject</Field.Label>
|
||||
<Input id="feedback-subject" bind:value={title} required />
|
||||
</Field.Field>
|
||||
<Field.Field>
|
||||
<Field.Label for="feedback-message">Message</Field.Label>
|
||||
<Textarea id="feedback-message" bind:value={description} required />
|
||||
</Field.Field>
|
||||
{#if !!errorMessage}
|
||||
<Field.Field>
|
||||
<Field.Error>{errorMessage}</Field.Error>
|
||||
</Field.Field>
|
||||
{/if}
|
||||
</Field.Group>
|
||||
<Button
|
||||
onclick={handleClick}
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
>
|
||||
Submit Feedback
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,87 @@
|
||||
<script>
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import * as Field from '@cio/ui/base/field';
|
||||
import { Input } from '@cio/ui/base/input';
|
||||
import { Textarea } from '@cio/ui/base/textarea';
|
||||
import { isFormValid } from '$lib/utils/is-form-valid';
|
||||
import { submitForm } from '$lib/utils/submit-form';
|
||||
import { Success } from '$lib/components';
|
||||
|
||||
let name = '';
|
||||
let email = '';
|
||||
let title = '';
|
||||
let description = '';
|
||||
|
||||
let isLoading = false;
|
||||
let errorMessage = '';
|
||||
let sent = false;
|
||||
|
||||
async function handleClick() {
|
||||
const data = { name, email, title, description };
|
||||
|
||||
if (!isFormValid(data)) {
|
||||
errorMessage = 'Please fill out all required fields.';
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading = true;
|
||||
|
||||
const formData = {
|
||||
title: `[HELP] ${title}`,
|
||||
description: `My name is ${JSON.stringify(name)}, email is ${JSON.stringify(
|
||||
email
|
||||
)}. \n\n${JSON.stringify(description)}.`,
|
||||
replyTo: email
|
||||
};
|
||||
|
||||
try {
|
||||
await submitForm(formData);
|
||||
sent = true;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if sent}
|
||||
<Success />
|
||||
{:else}
|
||||
<div class="w-full my-10 space-y-5">
|
||||
<Field.Group>
|
||||
<div class="flex flex-col md:flex-row gap-10">
|
||||
<Field.Field class="w-full">
|
||||
<Field.Label for="help-name">Your Name</Field.Label>
|
||||
<Input id="help-name" bind:value={name} required />
|
||||
</Field.Field>
|
||||
<Field.Field class="w-full">
|
||||
<Field.Label for="help-email">Your Email</Field.Label>
|
||||
<Input id="help-email" type="email" bind:value={email} required />
|
||||
</Field.Field>
|
||||
</div>
|
||||
<Field.Field>
|
||||
<Field.Label for="help-subject">Subject</Field.Label>
|
||||
<Input id="help-subject" bind:value={title} required />
|
||||
</Field.Field>
|
||||
<Field.Field>
|
||||
<Field.Label for="help-message">Message</Field.Label>
|
||||
<Textarea id="help-message" bind:value={description} required />
|
||||
</Field.Field>
|
||||
{#if !!errorMessage}
|
||||
<Field.Field>
|
||||
<Field.Error>{errorMessage}</Field.Error>
|
||||
</Field.Field>
|
||||
{/if}
|
||||
</Field.Group>
|
||||
<Button
|
||||
onclick={handleClick}
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
>
|
||||
Submit Request
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import { openModal, htmlBody } from './store';
|
||||
import * as Dialog from '@cio/ui/base/dialog';
|
||||
|
||||
interface Avatar {
|
||||
src: string;
|
||||
}
|
||||
|
||||
let isAddIcon = false;
|
||||
let hoveredIndex: number | null = null;
|
||||
|
||||
let avatars: Avatar[] = [
|
||||
{ src: 'avatar_a' },
|
||||
{ src: 'avatar_b' },
|
||||
{ src: 'avatar_c' },
|
||||
{ src: 'avatar_d' },
|
||||
{ src: 'avatar_e' },
|
||||
{ src: 'avatar_f' },
|
||||
{ src: 'avatar_g' },
|
||||
{ src: 'avatar_h' },
|
||||
{ src: 'avatar_i' },
|
||||
{ src: 'avatar_j' },
|
||||
{ src: 'avatar_k' },
|
||||
{ src: 'avatar_l' },
|
||||
{ src: 'avatar_m' },
|
||||
{ src: 'avatar_n' },
|
||||
{ src: 'avatar_o' },
|
||||
{ src: 'avatar_p' },
|
||||
{ src: 'avatar_q' },
|
||||
{ src: 'avatar_r' },
|
||||
{ src: 'avatar_s' },
|
||||
{ src: 'avatar_t' }
|
||||
];
|
||||
|
||||
function selectAvatar(avatar: Avatar) {
|
||||
$htmlBody.avatar = avatar.src;
|
||||
$openModal.avatar = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open={$openModal.avatar}>
|
||||
<Dialog.Content>
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Choose your Avatars</Dialog.Title>
|
||||
</Dialog.Header>
|
||||
|
||||
<!-- map for each avatar -->
|
||||
<div class="flex flex-wrap justify-evenly gap-y-3 mt-3">
|
||||
{#each avatars as avatar, index}
|
||||
<button
|
||||
on:click={() => {
|
||||
selectAvatar(avatar);
|
||||
}}
|
||||
on:mouseenter={() => {
|
||||
hoveredIndex = index;
|
||||
isAddIcon = true;
|
||||
}}
|
||||
on:mouseleave={() => {
|
||||
hoveredIndex = null;
|
||||
isAddIcon = false;
|
||||
}}
|
||||
class="relative w-[17%] flex justify-center items-center shadow-sm rounded-full hover:scale-110 transition-all duration-300"
|
||||
>
|
||||
<!-- + icon overlay -->
|
||||
{#if isAddIcon && hoveredIndex === index}
|
||||
<div
|
||||
class="w-full h-full flex justify-center items-center bg-white border absolute rounded-full opacity-[0.7] blur-md"
|
||||
></div>
|
||||
<img src="/free-tools/progress-report/hover-plus-icon.svg" alt="" class="absolute" />
|
||||
{/if}
|
||||
<!-- avatar -->
|
||||
<img
|
||||
src="https://assets.cdn.clsrio.com/progress-report/avatar/{avatar.src}.svg"
|
||||
alt=""
|
||||
class="w-full"
|
||||
/>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
<!-- -->
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
import * as Dialog from '@cio/ui/base/dialog';
|
||||
import { htmlBody, openModal } from './store';
|
||||
|
||||
interface Background {
|
||||
src: string;
|
||||
}
|
||||
|
||||
let backgrounds: Background[] = [
|
||||
{
|
||||
src: 'blue_tetiary_background'
|
||||
},
|
||||
{
|
||||
src: 'blue_secondary_background'
|
||||
},
|
||||
{
|
||||
src: 'blue_primary_background'
|
||||
},
|
||||
{
|
||||
src: 'purple_tetiary_background'
|
||||
},
|
||||
{
|
||||
src: 'purple_secondary_background'
|
||||
},
|
||||
{
|
||||
src: 'purple_primary_background'
|
||||
},
|
||||
{
|
||||
src: 'black_tetiary_background'
|
||||
},
|
||||
{
|
||||
src: 'black_secondary_background'
|
||||
},
|
||||
{
|
||||
src: 'black_primary_background'
|
||||
},
|
||||
{
|
||||
src: 'yellow_tetiary_background'
|
||||
},
|
||||
{
|
||||
src: 'yellow_secondary_background'
|
||||
},
|
||||
{
|
||||
src: 'yellow_primary_background'
|
||||
}
|
||||
];
|
||||
|
||||
function selectBackground(background: Background) {
|
||||
$htmlBody.background = background.src;
|
||||
$openModal.background = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open={$openModal.background}>
|
||||
<Dialog.Content class="h-full max-h-[60vh]">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Choose your Background</Dialog.Title>
|
||||
</Dialog.Header>
|
||||
|
||||
<!-- -->
|
||||
<div
|
||||
class="flex flex-wrap justify-between gap-y-3 mt-3 overflow-y-auto overflow-x-hidden h-[85%]"
|
||||
>
|
||||
{#each backgrounds as background}
|
||||
<button
|
||||
on:click={() => {
|
||||
selectBackground(background);
|
||||
}}
|
||||
class="flex w-[30%] items-center shadow-sm hover:scale-110 transition-all duration-300"
|
||||
>
|
||||
<img
|
||||
src="https://assets.cdn.clsrio.com/progress-report/backgrounds/{background.src}.webp"
|
||||
alt=""
|
||||
class=""
|
||||
/>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
<!-- -->
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
+8
-9
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { toPng } from 'html-to-image';
|
||||
import { nodeStore, htmlBody, openModal } from './store';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
|
||||
export let isDisabled: boolean;
|
||||
export let isDownloading: boolean;
|
||||
@@ -39,14 +40,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
disabled={isDownloading || !isDisabled ? true : false}
|
||||
on:click={convertToPng}
|
||||
class="{isDownloading || !isDisabled
|
||||
? 'bg-gray-200 cursor-not-allowed text-gray-600'
|
||||
: 'bg-[#0233BD] text-white'} {text === 'Generate Progress Report'
|
||||
? 'block md:hidden'
|
||||
: ''} mt-5 transition-all delay-100 text-white text-xs font-semibold w-full py-3 rounded-md"
|
||||
>{isDownloading ? 'Loading...' : text}</button
|
||||
disabled={isDownloading || !isDisabled}
|
||||
onclick={convertToPng}
|
||||
loading={isDownloading}
|
||||
class="{text === 'Generate Progress Report' ? 'block md:hidden' : ''} mt-5 w-full"
|
||||
>
|
||||
{isDownloading ? 'Loading...' : text}
|
||||
</Button>
|
||||
@@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import * as Dialog from '@cio/ui/base/dialog';
|
||||
import Report from './report.svelte';
|
||||
import { openModal } from './store';
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open={$openModal.fullscreen}>
|
||||
<Dialog.Content showCloseButton={false}>
|
||||
<!-- -->
|
||||
<Report />
|
||||
<!-- -->
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
+8
-14
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Modal from '$lib/Modal/Modal.svelte';
|
||||
import * as Dialog from '@cio/ui/base/dialog';
|
||||
import { htmlBody, openModal } from './store';
|
||||
|
||||
interface Mood {
|
||||
@@ -65,18 +65,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal bind:open={$openModal.mood}>
|
||||
<!-- header -->
|
||||
<div class="flex justify-between">
|
||||
<h1 class="text-sm font-semibold">Choose your Mood</h1>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => ($openModal.mood = false)}
|
||||
class="p-2 bg-[#F1F6FF] hover:scale-110 transition-all duration-300 rounded-full w-6"
|
||||
>
|
||||
<img src="/free-tools/progress-report/close-icon.svg" alt="Close icon" />
|
||||
</button>
|
||||
</div>
|
||||
<Dialog.Root bind:open={$openModal.mood}>
|
||||
<Dialog.Content class="w-full md:w-[30%] md:min-w-[500px]">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Choose your Mood</Dialog.Title>
|
||||
</Dialog.Header>
|
||||
|
||||
<!-- mapping -->
|
||||
<div class="flex flex-wrap justify-between gap-y-3 mt-3">
|
||||
@@ -97,4 +90,5 @@
|
||||
{/each}
|
||||
</div>
|
||||
<!-- -->
|
||||
</Modal>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
import { fly } from 'svelte/transition';
|
||||
import WinningLine from './WinningLine.svelte';
|
||||
import WinningLine from './winning-line.svelte';
|
||||
import { changeToComputer } from './store';
|
||||
|
||||
interface Game {
|
||||
@@ -0,0 +1,4 @@
|
||||
// Re-export all components for convenient importing
|
||||
// Usage: import { Footer, Navigation, Hero } from '$lib/components'
|
||||
export * from './components';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" context="module">
|
||||
import { img } from '$lib/custom/index';
|
||||
import { CustomImg as img } from '$lib/components';
|
||||
export { img };
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import PageSignupCTA from '$lib/PageSignupCTA/index.svelte';
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
import { PageSignupCTA, PageHeader } from '$lib/components';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import { Badge } from '@cio/ui/base/badge';
|
||||
|
||||
const benefitDetails = [
|
||||
{
|
||||
@@ -77,20 +78,15 @@
|
||||
<PageHeader
|
||||
className="flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-6 lg:px-28 md:px-16 px-8"
|
||||
>
|
||||
<div class="bg-[#DCE5FF] py-2 px-4 rounded-full border-[#C2D2FF] border-2 text-center">
|
||||
<p class=" text-sm md:text-base text-[#4B5563] text-center font-medium">
|
||||
Solution for Bootcamps
|
||||
</p>
|
||||
</div>
|
||||
<Badge class="py-2! px-4!" variant="outline">Solution for Bootcamps</Badge>
|
||||
|
||||
<div
|
||||
class="w-full flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-8"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-screen-xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1
|
||||
class="mx-auto m-0 text-4xl lg:text-7xl md:text-6xl font-bold text-slate-900 lg:text-center"
|
||||
class="w-full max-w-7xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1 class="mx-auto m-0 text-4xl md:text-6xl text-slate-900 lg:text-center">
|
||||
The platform for fast
|
||||
<span class="text-blue-700 relative"> growing bootcamps</span>
|
||||
</h1>
|
||||
@@ -99,13 +95,9 @@
|
||||
everything you need to run a cutting-edge, high-impact bootcamp.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/signup"
|
||||
target="_blank"
|
||||
class="py-5 px-6 bg-[#1D4ED8] border-transparent outline-transparent rounded-lg text-white text-lg font-semibold"
|
||||
>
|
||||
<Button href="/signup" target="_blank" rel="noopener noreferrer nofollow">
|
||||
Get Started for Free 🚀
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
@@ -115,11 +107,10 @@
|
||||
<div
|
||||
class="w-full gap-y-4 flex flex-col lg:items-center items-start lg:justify-center justify-start"
|
||||
>
|
||||
<div class="w-fit py-2 px-4 rounded-full border-[#C2D2FF] border-2 text-center">
|
||||
<p class=" text-sm md:text-base font-medium">Our benefits</p>
|
||||
</div>
|
||||
<Badge class="py-2! px-4!" variant="outline">Our benefits</Badge>
|
||||
|
||||
<div
|
||||
class="lg:w-full max-w-screen-xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
class="lg:w-full max-w-7xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1 class="text-3xl md:text-5xl font-bold">Inspire Student Success</h1>
|
||||
<p class="text-[#334155] text-sm md:text-base font-light lg:text-center md:w-[80%]">
|
||||
@@ -130,7 +121,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full max-w-screen-xl flex lg:flex-row flex-col lg:justify-between justify-start gap-6 lg:items-center items-start"
|
||||
class="w-full max-w-7xl flex lg:flex-row flex-col lg:justify-between justify-start gap-6 lg:items-center items-start"
|
||||
>
|
||||
{#each benefitDetails as benefits}
|
||||
<div class="w-full md:p-6 p-4 border border-[#D9E0F5] rounded-2xl flex flex-col gap-y-6">
|
||||
@@ -164,11 +155,10 @@
|
||||
class="w-full bg-[#F1F6FF] flex flex-col items-center justify-center gap-y-16 lg:py-24 py-16 lg:px-28 md:px-16 px-8"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-screen-xl gap-y-4 flex flex-col lg:items-center items-start lg:justify-center justify-start"
|
||||
class="w-full max-w-7xl gap-y-4 flex flex-col lg:items-center items-start lg:justify-center justify-start"
|
||||
>
|
||||
<div class="w-fit py-2 px-4 rounded-full border-[#C2D2FF] border-2 text-center">
|
||||
<p class=" text-sm md:text-base font-medium">Explore our features</p>
|
||||
</div>
|
||||
<Badge class="py-2! px-4!" variant="outline">Explore our features</Badge>
|
||||
|
||||
<div
|
||||
class="w-full flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
@@ -181,9 +171,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full max-w-screen-xl flex flex-col justify-start items-center lg:gap-y-24 gap-y-16"
|
||||
>
|
||||
<div class="w-full max-w-7xl flex flex-col justify-start items-center lg:gap-y-24 gap-y-16">
|
||||
{#each featureDetails as feature, i}
|
||||
<div
|
||||
class="w-full lg:w-[70%] flex lg:flex-row {i % 2 !== 0
|
||||
@@ -206,21 +194,17 @@
|
||||
</div>
|
||||
|
||||
{#if feature.ctaLink}
|
||||
<a
|
||||
class="py-4 px-8 border border-[#1D4ED8] outline-transparent rounded text-[#1D4ED8] text-sm font-semibold"
|
||||
href={feature.ctaLink}
|
||||
target="_blank noopener noreferrer"
|
||||
>
|
||||
<Button variant="outline" href={feature.ctaLink} target="_blank noopener noreferrer">
|
||||
{feature.ctaLabel}
|
||||
</a>
|
||||
</Button>
|
||||
{:else}
|
||||
<button
|
||||
class="py-4 px-8 border border-[#1D4ED8] outline-transparent rounded text-[#1D4ED8] text-sm font-semibold"
|
||||
<Button
|
||||
variant="outline"
|
||||
data-cal-config="'layout':'month_view'"
|
||||
data-cal-link="classroomio/demo"
|
||||
>
|
||||
Book a Demo
|
||||
</button>
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import PageSignupCTA from '$lib/PageSignupCTA/index.svelte';
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
import { PageSignupCTA, PageHeader } from '$lib/components';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import { Badge } from '@cio/ui/base/badge';
|
||||
|
||||
const benefitDetails = [
|
||||
{
|
||||
@@ -61,20 +62,15 @@
|
||||
<PageHeader
|
||||
className="flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-6 lg:px-28 md:px-16 px-8"
|
||||
>
|
||||
<div class="bg-[#DCE5FF] py-2 px-4 rounded-full border-[#C2D2FF] border-2 text-center">
|
||||
<p class=" text-sm md:text-base text-[#4B5563] text-center font-medium">
|
||||
Solution for Customer Education
|
||||
</p>
|
||||
</div>
|
||||
<Badge class="py-2! px-4!" variant="outline">Solution for Customer Education</Badge>
|
||||
|
||||
<div
|
||||
class="w-full flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-8"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-screen-xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1
|
||||
class="w-[95%] mx-auto m-0 text-4xl lg:text-7xl md:text-6xl font-bold text-slate-900 lg:text-center"
|
||||
class="w-full max-w-7xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1 class="w-[95%] mx-auto m-0 text-4xl md:text-6xl text-slate-900 lg:text-center">
|
||||
Drive retention and revenue with
|
||||
<span class="text-blue-700 relative">Customer Education</span>
|
||||
</h1>
|
||||
@@ -84,13 +80,9 @@
|
||||
table.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/signup"
|
||||
target="_blank"
|
||||
class="py-5 px-6 bg-[#1D4ED8] border-transparent outline-transparent rounded-lg text-white text-lg font-semibold"
|
||||
>
|
||||
<Button href="/signup" target="_blank" rel="noopener noreferrer nofollow">
|
||||
Get Started for Free 🚀
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
@@ -100,11 +92,9 @@
|
||||
<div
|
||||
class="w-full gap-y-4 flex flex-col lg:items-center items-start lg:justify-center justify-start"
|
||||
>
|
||||
<div class="w-fit py-2 px-4 rounded-full border-[#C2D2FF] border-2 text-center">
|
||||
<p class="text-sm md:text-base font-medium">Our benefits</p>
|
||||
</div>
|
||||
<Badge class="py-2! px-4!" variant="outline">Our benefits</Badge>
|
||||
<div
|
||||
class="lg:w-full max-w-screen-xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
class="lg:w-full max-w-7xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1 class="text-3xl md:text-5xl font-bold">The customer education toolkit</h1>
|
||||
<p class="text-[#334155] text-sm md:text-base font-light lg:text-center md:w-[80%]">
|
||||
@@ -115,7 +105,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full max-w-screen-xl flex lg:flex-row flex-col lg:justify-between justify-start gap-6 lg:items-center items-start"
|
||||
class="w-full max-w-7xl flex lg:flex-row flex-col lg:justify-between justify-start gap-6 lg:items-center items-start"
|
||||
>
|
||||
{#each benefitDetails as benefits}
|
||||
<div class="w-full md:p-6 p-4 border border-[#D9E0F5] rounded-2xl flex flex-col gap-y-6">
|
||||
@@ -151,7 +141,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="lg:w-full max-w-screen-xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
class="lg:w-full max-w-7xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1 class="text-3xl md:text-5xl font-bold">Why Choose Us For Education Needs</h1>
|
||||
<p class="text-[#334155] text-sm md:text-base font-light lg:text-center md:w-[80%]">
|
||||
@@ -161,9 +151,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full max-w-screen-xl flex flex-col justify-start items-center lg:gap-y-24 gap-y-16"
|
||||
>
|
||||
<div class="w-full max-w-7xl flex flex-col justify-start items-center lg:gap-y-24 gap-y-16">
|
||||
{#each featureDetails as feature, i}
|
||||
<div
|
||||
class="w-full lg:w-[70%] flex lg:flex-row {i % 2 !== 0
|
||||
@@ -183,13 +171,13 @@
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="py-4 px-8 border border-[#1D4ED8] outline-transparent rounded text-[#1D4ED8] text-sm font-semibold"
|
||||
<Button
|
||||
variant="outline"
|
||||
data-cal-config="'layout':'month_view'"
|
||||
data-cal-link="classroomio/demo"
|
||||
>
|
||||
Book a Demo
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import PageSignupCTA from '$lib/PageSignupCTA/index.svelte';
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
import { PageSignupCTA, PageHeader } from '$lib/components';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import { Badge } from '@cio/ui/base/badge';
|
||||
|
||||
const benefitDetails = [
|
||||
{
|
||||
@@ -57,20 +58,15 @@
|
||||
<PageHeader
|
||||
className="flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-6 lg:px-28 md:px-16 px-8"
|
||||
>
|
||||
<div class="bg-[#DCE5FF] py-2 px-4 rounded-full border-[#C2D2FF] border-2 text-center">
|
||||
<p class=" text-sm md:text-base text-[#4B5563] text-center font-medium">
|
||||
Unlock your team's potential
|
||||
</p>
|
||||
</div>
|
||||
<Badge class="py-2! px-4!" variant="outline">Unlock your team's potential</Badge>
|
||||
|
||||
<div
|
||||
class="w-full flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-8"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-screen-xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1
|
||||
class="mx-auto m-0 text-4xl lg:text-7xl md:text-6xl font-bold text-slate-900 lg:text-center"
|
||||
class="w-full max-w-7xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1 class="mx-auto m-0 text-4xl md:text-6xl text-slate-900 lg:text-center">
|
||||
Employee training software for
|
||||
<br /><span class="text-blue-700 relative">modern teams</span>
|
||||
</h1>
|
||||
@@ -79,23 +75,15 @@
|
||||
quickly.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/signup"
|
||||
target="_blank"
|
||||
class="py-5 px-6 bg-[#1D4ED8] border-transparent outline-transparent rounded-lg text-white text-lg font-semibold"
|
||||
>
|
||||
Get Started for Free 🚀
|
||||
</a>
|
||||
<Button href="/signup" target="_blank">Get Started for Free 🚀</Button>
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<section
|
||||
class="w-full max-w-screen-xl lg:py-24 py-16 lg:px-28 md:px-16 px-8 flex lg:flex-row flex-col lg:items-center lg:justify-between justify-center gap-x-6 gap-y-8"
|
||||
class="w-full max-w-7xl lg:py-24 py-16 lg:px-28 md:px-16 px-8 flex lg:flex-row flex-col lg:items-center lg:justify-between justify-center gap-x-6 gap-y-8"
|
||||
>
|
||||
<div class="w-full lg:w-[35%] gap-y-4 flex flex-col items-start justify-start">
|
||||
<div class="w-fit py-2 px-4 rounded-full border-[#C2D2FF] border-2 text-center">
|
||||
<p class=" text-sm md:text-base font-medium">Amazing benefits</p>
|
||||
</div>
|
||||
<Badge variant="outline">Amazing benefits</Badge>
|
||||
|
||||
<div class="md:w-full flex flex-col items-start justify-start gap-y-4">
|
||||
<h1 class="font-bold text-4xl">Unlock your team's potential</h1>
|
||||
@@ -134,13 +122,10 @@
|
||||
<div
|
||||
class="w-full gap-y-4 flex flex-col lg:items-center items-start lg:justify-center justify-start"
|
||||
>
|
||||
<div class="w-fit py-2 px-4 rounded-full border-[#C2D2FF] border-2 text-center">
|
||||
<p class="text-sm md:text-base text-[#4B5563] text-center font-medium">
|
||||
Explore our features
|
||||
</p>
|
||||
</div>
|
||||
<Badge class="py-2! px-4!" variant="outline">Explore our features</Badge>
|
||||
|
||||
<div
|
||||
class="lg:w-full max-w-screen-xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
class="lg:w-full max-w-7xl flex flex-col lg:items-center items-start lg:justify-center justify-start gap-y-4"
|
||||
>
|
||||
<h1 class="text-3xl md:text-5xl font-bold">What makes us the right choice</h1>
|
||||
<p class="text-[#334155] text-sm md:text-base font-light lg:text-center md:w-[80%]">
|
||||
@@ -150,9 +135,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full max-w-screen-xl flex flex-col justify-start items-center lg:gap-y-24 gap-16 p-2"
|
||||
>
|
||||
<div class="w-full max-w-7xl flex flex-col justify-start items-center lg:gap-y-24 gap-16 p-2">
|
||||
{#each featureDetails as feature, i}
|
||||
<div
|
||||
class="w-full lg:w-[70%] flex lg:flex-row {i % 2 !== 0
|
||||
@@ -173,13 +156,13 @@
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="py-4 px-8 border border-[#1D4ED8] outline-transparent rounded text-[#1D4ED8] text-sm font-semibold"
|
||||
<Button
|
||||
variant="outline"
|
||||
data-cal-config="'layout':'month_view'"
|
||||
data-cal-link="classroomio/demo"
|
||||
>
|
||||
Book a Demo
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -198,7 +181,7 @@
|
||||
class="w-full bg-[#040F2D] flex flex-col items-center justify-start gap-y-16 lg:py-24 py-16 lg:px-28 md:px-16 px-8"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-screen-xl flex lg:flex-row-reverse flex-col-reverse lg:justify-between justify-start lg:items-center items-start gap-y-8"
|
||||
class="w-full max-w-7xl flex lg:flex-row-reverse flex-col-reverse lg:justify-between justify-start lg:items-center items-start gap-y-8"
|
||||
>
|
||||
<div class="w-full lg:w-[40%] gap-y-4 flex flex-col items-start justify-start">
|
||||
<div class="md:w-full flex flex-col items-start justify-start gap-y-4">
|
||||
@@ -209,13 +192,7 @@
|
||||
Companies get the opportunity to request custom features from us.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/demo"
|
||||
target="_blank"
|
||||
class="py-4 px-8 border border-tranparent outline-transparent bg-white rounded text-[#040F2D] text-sm font-semibold"
|
||||
>
|
||||
Talk to sales
|
||||
</a>
|
||||
<Button href="/demo" target="_blank" variant="outline">Talk to sales</Button>
|
||||
</div>
|
||||
|
||||
<img
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<script>
|
||||
import { dev } from '$app/environment';
|
||||
import { page } from '$app/state';
|
||||
import Footer from '$lib/Footer/Footer.svelte';
|
||||
import Navigation from '$lib/Navigation/Navigation.svelte';
|
||||
import NotFound from '$lib/NotFound/NotFound.svelte';
|
||||
import { Footer, Navigation, NotFound } from '$lib/components';
|
||||
import extend from 'just-extend';
|
||||
import { onMount } from 'svelte';
|
||||
import { MetaTags } from 'svelte-meta-tags';
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
<script>
|
||||
import Hero from '$lib/Home/Hero.svelte';
|
||||
import CoreFeatures from '$lib/Home/CoreFeatures.svelte';
|
||||
import Faq from '$lib/Home/Faq.svelte';
|
||||
import StudentDashboard from '$lib/Home/StudentDashboard.svelte';
|
||||
import MoreFeatures from '$lib/Home/MoreFeatures.svelte';
|
||||
import Testimonial from '$lib/Home/Testimonial.svelte';
|
||||
import { Hero, CoreFeatures, Faq, StudentDashboard, MoreFeatures, Testimonial } from '$lib/components';
|
||||
import { PUBLIC_ENABLE_FAQ } from '$env/static/public';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { BlogPost } from '$lib/types';
|
||||
import type { BlogPost } from '$lib/utils/types';
|
||||
|
||||
async function getBlogPosts() {
|
||||
let blogPosts: BlogPost[] = [];
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { PUBLIC_SERVER_URL } from '$env/static/public';
|
||||
|
||||
const sendEmail = async (subject: string, content: string, replyTo?: string) => {
|
||||
const sendEmail =
|
||||
(sFetch: typeof fetch) => async (subject: string, content: string, replyTo?: string) => {
|
||||
try {
|
||||
const response = await fetch(`${PUBLIC_SERVER_URL}/sendEmail`, {
|
||||
const response = await sFetch(`${PUBLIC_SERVER_URL}/sendEmail`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -26,13 +27,13 @@ const sendEmail = async (subject: string, content: string, replyTo?: string) =>
|
||||
console.error('Error sending email', error);
|
||||
return 'failed';
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export async function POST({ request }) {
|
||||
export async function POST({ request, fetch }) {
|
||||
try {
|
||||
const { title, description, replyTo } = await request.json();
|
||||
console.log(title, description, replyTo);
|
||||
const result = await sendEmail(title, description, replyTo);
|
||||
const result = await sendEmail(fetch)(title, description, replyTo);
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<script>
|
||||
import PageSignupCTA from '$lib/PageSignupCTA/index.svelte';
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
import BlogListItem from '$lib/Blog/BlogListItem.svelte';
|
||||
import { PageSignupCTA, PageHeader, BlogListItem } from '$lib/components';
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
@@ -12,9 +10,7 @@
|
||||
|
||||
<section>
|
||||
<PageHeader className="flex flex-col items-center justify-center text-center">
|
||||
<h1
|
||||
class="mx-auto text-4xl md:text-7xl lg:text-6xl font-bold text-slate-900 flex flex-col items-center"
|
||||
>
|
||||
<h1 class="mx-auto text-4xl md:text-6xl text-slate-900 flex flex-col items-center">
|
||||
<span>What's new on</span>
|
||||
<span class="text-blue-700 relative">ClassroomIO?</span>
|
||||
</h1>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BlogPost } from '$lib/types';
|
||||
import type { BlogPost } from '$lib/utils/types';
|
||||
|
||||
/** @type {import('./$types').PageLoad} */
|
||||
export async function load({ fetch }) {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { ChevronLeft } from 'carbon-icons-svelte';
|
||||
import Chip from '$lib/Chip/Chip.svelte';
|
||||
import { formatDate } from '$lib/utils/formatDate';
|
||||
import BlogListItem from '$lib/Blog/BlogListItem.svelte';
|
||||
import ChevronLeft from '@lucide/svelte/icons/chevron-left';
|
||||
import { Badge } from '@cio/ui/base/badge';
|
||||
import { formatDate } from '$lib/utils/format-date';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
|
||||
import { BlogListItem } from '$lib/components';
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
@@ -11,13 +13,13 @@
|
||||
{#if data}
|
||||
<article class="py-16">
|
||||
<!-- Title -->
|
||||
<hgroup class="flex flex-col items-center justify-center text-center w-full">
|
||||
<hgroup class="flex w-full flex-col items-center justify-center text-center">
|
||||
<p class="text-sm text-gray-500">{formatDate(data.meta.date)}</p>
|
||||
<p class="font-bold text-3xl py-2 text-center md:w-[60%]">{@html data.meta.title}</p>
|
||||
<p class="py-2 text-center text-3xl font-bold md:w-[60%]">{@html data.meta.title}</p>
|
||||
</hgroup>
|
||||
<main class="mx-auto max-w-screen-md px-4 lg:px-8">
|
||||
<div class="flex items-center justify-start gap-4 my-2 border-y border-gray-200 py-4">
|
||||
<img loading="lazy" src={data.meta.avatar} alt="avatar" class="w-10 h-10 rounded-full" />
|
||||
<main class="mx-auto max-w-3xl px-4 lg:px-8">
|
||||
<div class="my-2 flex items-center justify-start gap-4 border-y border-gray-200 py-4">
|
||||
<img loading="lazy" src={data.meta.avatar} alt="avatar" class="h-10 w-10 rounded-full" />
|
||||
<span>
|
||||
<p class="font-semibold">{data.meta.author}</p>
|
||||
<p class="text-gray-500">{data.meta.role}</p>
|
||||
@@ -25,12 +27,12 @@
|
||||
</div>
|
||||
|
||||
<!-- Post -->
|
||||
<div class="prose pt-2 pb-4 border-b-2 border-gray-200">
|
||||
<div class="prose border-b-2 border-gray-200 pb-4 pt-2">
|
||||
<svelte:component this={data.content} />
|
||||
<!-- Tags -->
|
||||
<div class="flex gap-2 py-4">
|
||||
{#each data.meta.tags as tag}
|
||||
<Chip label={tag} />
|
||||
<Badge variant="outline">{tag}</Badge>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,7 +42,7 @@
|
||||
<p class="text-xl font-semibold">Related Posts</p>
|
||||
<ul class="flex items-start justify-start gap-3 overflow-x-scroll">
|
||||
{#each data.relatedPosts as post}
|
||||
<li class="py-10 min-w-[80%] sm:w-80 sm:min-w-0">
|
||||
<li class="min-w-[80%] py-10 sm:w-80 sm:min-w-0">
|
||||
<BlogListItem {post} isRecommended />
|
||||
</li>
|
||||
{/each}
|
||||
@@ -48,12 +50,9 @@
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<a
|
||||
href="/blog"
|
||||
class="flex font-medium items-center gap-2 text-slate-700 text-lg underline my-10"
|
||||
>
|
||||
<ChevronLeft class=" w-6 h-6 font-medium" /> Back to all posts
|
||||
</a>
|
||||
<Button href="/blog" variant="link">
|
||||
<ChevronLeft /> Back to all posts
|
||||
</Button>
|
||||
</main>
|
||||
</article>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { BlogPost, RawBlogPost } from '$lib/types';
|
||||
import type { BlogPost, RawBlogPost } from '$lib/utils/types';
|
||||
import type { MetaTagsProps } from 'svelte-meta-tags';
|
||||
|
||||
/** @type {import('./$types').PageLoad} */
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import ArrowUpRight from 'carbon-icons-svelte/lib/ArrowUpRight.svelte';
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
import ArrowUpRight from '@lucide/svelte/icons/arrow-up-right';
|
||||
|
||||
import FeedbackForm from '$lib/Contact/FeedbackForm.svelte';
|
||||
import HelpForm from '$lib/Contact/HelpForm.svelte';
|
||||
import { FORM_TYPE } from '$lib/types';
|
||||
import type { FORM_TYPE_KEY } from '$lib/types';
|
||||
import { FORM_TYPE } from '$lib/utils/types';
|
||||
import type { FORM_TYPE_KEY } from '$lib/utils/types';
|
||||
|
||||
import HelpForm from '$lib/features/contact/help-form.svelte';
|
||||
import { PageHeader } from '$lib/components';
|
||||
import FeedbackForm from '$lib/features/contact/feedback-form.svelte';
|
||||
|
||||
let currentForm: FORM_TYPE_KEY;
|
||||
|
||||
@@ -45,40 +46,38 @@
|
||||
|
||||
<section>
|
||||
<PageHeader className="flex flex-col items-center justify-center bg-[#F5F8FE] text-center">
|
||||
<div class="mb-2 ml-[5%] lg:ml-0 flex w-full items-center justify-center">
|
||||
<div class="mb-2 ml-[5%] flex w-full items-center justify-center lg:ml-0">
|
||||
<span
|
||||
style="color: rgb(75, 85, 99);"
|
||||
class="rounded-full border-2 border-[#C2D2FF] px-4 py-1 text-sm font-medium bg-[#DCE5FF] text-blue-700"
|
||||
class="rounded-full border-2 border-[#C2D2FF] bg-[#DCE5FF] px-4 py-1 text-sm font-medium text-blue-700"
|
||||
>
|
||||
Contact Us
|
||||
</span>
|
||||
</div>
|
||||
<h1
|
||||
class="mx-auto text-4xl md:text-7xl lg:text-6xl font-bold text-slate-900 flex flex-col items-center"
|
||||
>
|
||||
<h1 class="mx-auto flex flex-col items-center text-4xl text-slate-900 md:text-6xl">
|
||||
<span>Get in touch with <span class="text-blue-700">Us</span></span>
|
||||
</h1>
|
||||
<p
|
||||
class="w-[90%] md:w-[60%] max-w-3xl text-center font-normal text-lg text-slate-700 mt-10 lg:mt-7"
|
||||
class="mt-10 w-[90%] max-w-3xl text-center text-lg font-normal text-slate-700 md:w-[60%] lg:mt-7"
|
||||
>
|
||||
Report any bugs you encounter, request new features you'd like to see, or ask any questions
|
||||
you have about ClassroomIO
|
||||
</p>
|
||||
</PageHeader>
|
||||
|
||||
<div class="my-20 mx-auto">
|
||||
<div class="mx-auto my-20">
|
||||
<!-- Content here -->
|
||||
<div
|
||||
class="mx-auto w-[90%] lg:w-[60%] place-items-center grid grid-cols-1 md:grid-cols-2 gap-10"
|
||||
class="mx-auto grid w-[90%] grid-cols-1 place-items-center gap-10 md:grid-cols-2 lg:w-[60%]"
|
||||
>
|
||||
{#each formList as list}
|
||||
<button
|
||||
on:click={() => onChange(list.id, list.to)}
|
||||
class="text-start border-[1.5px] w-full rounded-md p-3 {currentForm === list.id
|
||||
class="w-full rounded-md border-[1.5px] p-3 text-start {currentForm === list.id
|
||||
? `border-[#0233BD]`
|
||||
: `border-gray-200`} flex items-center justify-between"
|
||||
>
|
||||
<p class="text-lg text-slate-700 font-normal">{list.title}</p>
|
||||
<p class="text-lg font-normal text-slate-700">{list.title}</p>
|
||||
{#if list.to}
|
||||
<ArrowUpRight size={16} />
|
||||
{/if}
|
||||
@@ -87,7 +86,7 @@
|
||||
</div>
|
||||
|
||||
{#if currentForm === FORM_TYPE[currentForm]}
|
||||
<div class="mx-auto w-[90%] lg:w-[60%] px-5">
|
||||
<div class="mx-auto w-[90%] px-5 lg:w-[60%]">
|
||||
{#if currentForm === FORM_TYPE.HELP}
|
||||
<HelpForm />
|
||||
{:else if currentForm === FORM_TYPE.FEEDBACK}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
import { PageHeader } from '$lib/components';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
@@ -35,18 +36,14 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-left px-6 mt-3">
|
||||
<a
|
||||
<Button
|
||||
href={friend.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-block w-full"
|
||||
>
|
||||
<button
|
||||
class="font-semibold text-base text-white bg-gray-800 hover:bg-gray-900 hover:shadow-xl transition-all delay-100 px-4 py-3 rounded-md"
|
||||
variant="outline"
|
||||
>
|
||||
Learn more
|
||||
</button>
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { OssFriend } from '$lib/types';
|
||||
import type { OssFriend } from '$lib/utils/types';
|
||||
|
||||
export async function load() {
|
||||
const friends: OssFriend[] = [
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script>
|
||||
import Check from '@lucide/svelte/icons/check';
|
||||
import { fade } from 'svelte/transition';
|
||||
import Checkmark from 'carbon-icons-svelte/lib/Checkmark.svelte';
|
||||
|
||||
import { PLANS } from '@cio/utils/plans';
|
||||
import PageSignupCTA from '$lib/PageSignupCTA/index.svelte';
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
|
||||
import { PageSignupCTA, PageHeader } from '$lib/components';
|
||||
|
||||
let isYearlyPlan = false;
|
||||
|
||||
@@ -18,16 +19,14 @@
|
||||
|
||||
<section>
|
||||
<PageHeader className="flex flex-col items-center justify-center text-center">
|
||||
<h1
|
||||
class="mx-auto text-4xl md:text-7xl lg:text-6xl font-bold text-slate-900 flex flex-col items-center"
|
||||
>
|
||||
<h1 class="mx-auto flex flex-col items-center text-4xl text-slate-900 md:text-6xl">
|
||||
<span>There is room for</span>
|
||||
<span class="text-blue-700 relative">Everyone.</span>
|
||||
<span class="relative text-blue-700">Everyone.</span>
|
||||
</h1>
|
||||
<p class="w-[90%] md:w-[60%] text-center font-normal text-lg text-slate-700 mt-10 lg:mt-7">
|
||||
<p class="mt-10 w-[90%] text-center text-lg font-normal text-slate-700 md:w-[60%] lg:mt-7">
|
||||
You get a customizable LMS, AI integration for productive educators and many more...
|
||||
</p>
|
||||
<div class="relative mt-10 flex items-center rounded-[30px] border-[2px] p-[2px] lg:scale-100">
|
||||
<div class="relative mt-10 flex items-center rounded-[30px] border-2 p-[2px] lg:scale-100">
|
||||
<button
|
||||
style="background-color: {isYearlyPlan ? 'initial' : '#1D4EE2'}; color: {isYearlyPlan
|
||||
? '#5e636b'
|
||||
@@ -71,7 +70,7 @@
|
||||
{PLANS.BASIC.NAME}
|
||||
</p>
|
||||
|
||||
<p class="lg:text-base mt-4 text-sm font-light leading-6 text-gray-500 lg:leading-6">
|
||||
<p class="mt-4 text-sm font-light leading-6 text-gray-500 lg:text-base lg:leading-6">
|
||||
{PLANS.BASIC.DESCRIPTION}
|
||||
</p>
|
||||
|
||||
@@ -81,7 +80,7 @@
|
||||
<span class="text-base">Free forever</span>
|
||||
|
||||
<a
|
||||
class="text-base mt-10 block w-full rounded-md bg-slate-900 py-3 text-center font-medium text-white hover:bg-slate-700 lg:rounded-md lg:py-3 lg:text-lg lg:font-semibold"
|
||||
class="mt-10 block w-full rounded-md bg-slate-900 py-3 text-center text-base font-medium text-white hover:bg-slate-700 lg:rounded-md lg:py-3 lg:text-lg lg:font-semibold"
|
||||
href={PLANS.BASIC.CTA.LINK}
|
||||
target="_blank"
|
||||
>
|
||||
@@ -91,11 +90,7 @@
|
||||
<ul class="mt-8 space-y-2 text-sm text-gray-600 lg:space-y-5 xl:mt-10">
|
||||
{#each PLANS.BASIC.FEATURES as feature}
|
||||
<li class="flex items-center">
|
||||
<Checkmark
|
||||
size={24}
|
||||
fill="#1D4EE2"
|
||||
class="mr-2 scale-[70%] lg:mr-3 lg:scale-[100%]"
|
||||
/>
|
||||
<Check size={24} fill="#1D4EE2" class="mr-2 scale-[70%] lg:mr-3 lg:scale-[100%]" />
|
||||
{feature}
|
||||
</li>
|
||||
{/each}
|
||||
@@ -108,7 +103,7 @@
|
||||
{PLANS.EARLY_ADOPTER.NAME}
|
||||
</p>
|
||||
|
||||
<p class="lg:text-base mt-4 text-sm font-light leading-6 text-white lg:leading-6">
|
||||
<p class="mt-4 text-sm font-light leading-6 text-white lg:text-base lg:leading-6">
|
||||
{PLANS.EARLY_ADOPTER.DESCRIPTION}
|
||||
</p>
|
||||
|
||||
@@ -125,7 +120,7 @@
|
||||
{/key}
|
||||
|
||||
<a
|
||||
class="text-base mt-10 block w-full rounded-md bg-white py-3 text-center font-medium text-slate-900 hover:bg-indigo-50 lg:rounded-md lg:py-3 lg:text-lg lg:font-semibold"
|
||||
class="mt-10 block w-full rounded-md bg-white py-3 text-center text-base font-medium text-slate-900 hover:bg-indigo-50 lg:rounded-md lg:py-3 lg:text-lg lg:font-semibold"
|
||||
href={PLANS.EARLY_ADOPTER.CTA.LINK}
|
||||
target="_blank"
|
||||
>
|
||||
@@ -134,7 +129,7 @@
|
||||
<ul class="mt-8 space-y-3 text-sm font-light text-white lg:space-y-5 xl:mt-10">
|
||||
{#each PLANS.EARLY_ADOPTER.FEATURES as features}
|
||||
<li class="flex items-center">
|
||||
<Checkmark size={24} fill="#fff" class="mr-2 scale-[70%] lg:mr-3 lg:scale-[100%]" />
|
||||
<Check size={24} fill="#fff" class="mr-2 scale-[70%] lg:mr-3 lg:scale-[100%]" />
|
||||
{features}
|
||||
</li>
|
||||
{/each}
|
||||
@@ -146,14 +141,14 @@
|
||||
<p class="text-lg font-semibold leading-8 text-gray-900 lg:text-xl">
|
||||
{PLANS.ENTERPRISE.NAME}
|
||||
</p>
|
||||
<p class="lg:text-base mt-4 text-sm font-light leading-6 text-gray-500 lg:leading-6">
|
||||
<p class="mt-4 text-sm font-light leading-6 text-gray-500 lg:text-base lg:leading-6">
|
||||
{PLANS.ENTERPRISE.DESCRIPTION}
|
||||
</p>
|
||||
<p class="mt-6 flex items-baseline gap-x-1 text-xl font-medium lg:mt-6 lg:text-xl">
|
||||
Request Pricing
|
||||
</p>
|
||||
<button
|
||||
class="text-base mt-10 w-full rounded-md bg-slate-900 py-3 font-medium text-white hover:bg-slate-700 lg:rounded-md lg:py-3 lg:text-lg lg:font-semibold"
|
||||
class="mt-10 w-full rounded-md bg-slate-900 py-3 text-base font-medium text-white hover:bg-slate-700 lg:rounded-md lg:py-3 lg:text-lg lg:font-semibold"
|
||||
data-cal-config={"{'layout':'month_view'}"}
|
||||
data-cal-link="classroomio/enterprise"
|
||||
>
|
||||
@@ -163,11 +158,7 @@
|
||||
<ul class="mt-8 space-y-2 text-sm text-gray-600 lg:space-y-5 xl:mt-10">
|
||||
{#each PLANS.ENTERPRISE.FEATURES as features}
|
||||
<li class="flex items-center">
|
||||
<Checkmark
|
||||
size={24}
|
||||
fill="#1D4EE2"
|
||||
class="mr-2 scale-[70%] lg:mr-3 lg:scale-[100%]"
|
||||
/>
|
||||
<Check size={24} fill="#1D4EE2" class="mr-2 scale-[70%] lg:mr-3 lg:scale-[100%]" />
|
||||
{features}
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<script>
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
import Timeline from '$lib/Timeline/Timeline.svelte';
|
||||
import PageSignupCTA from '$lib/PageSignupCTA/index.svelte';
|
||||
import { PageHeader, Timeline, PageSignupCTA } from '$lib/components';
|
||||
|
||||
const newFeatures = [
|
||||
{
|
||||
@@ -67,13 +65,11 @@
|
||||
</svelte:head>
|
||||
|
||||
<PageHeader className="flex flex-col items-center justify-center text-center">
|
||||
<h1 class="text-start md:text-center text-5xl md:text-7xl font-bold text-slate-900">
|
||||
Product Roadmap
|
||||
</h1>
|
||||
<h1 class="text-start md:text-center text-4xl md:text-6xl text-slate-900">Product Roadmap</h1>
|
||||
<p class="text-start md:text-center w-[80%] md:w-[40%] text-slate-700 mt-10">
|
||||
This is a live document, and will be updated regularly. To submit an idea, or to view a list of
|
||||
feature requests and ideas that we're considering, check out our <a
|
||||
href="https://github.com/rotimi-best/classroomio/discussion"
|
||||
href="https://github.com/classroomio/classroomio/discussion"
|
||||
class="underline">GitHub Discussions</a
|
||||
>
|
||||
</p>
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { slide } from 'svelte/transition';
|
||||
import PageSignupCTA from '$lib/PageSignupCTA/index.svelte';
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
import { PageSignupCTA, PageHeader } from '$lib/components';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
|
||||
const whyStart = [
|
||||
{
|
||||
src: '/teach/teach-from-anywhere.svg',
|
||||
header: 'Fully Online; Teach from anywhere',
|
||||
subText:
|
||||
' Facilitate a subject from the comfort of your space, engaging students from highly diverse background'
|
||||
subText: ' Facilitate a subject from the comfort of your space, engaging students from highly diverse background'
|
||||
},
|
||||
{
|
||||
src: '/teach/get-paid.svg',
|
||||
header: 'Get paid extra commission',
|
||||
subText:
|
||||
'Get paid for the course you design and earn extra comission based on course perfomance'
|
||||
subText: 'Get paid for the course you design and earn extra comission based on course perfomance'
|
||||
},
|
||||
{
|
||||
src: '/teach/inspire-learners.svg',
|
||||
header: 'Inspire learners',
|
||||
subText:
|
||||
'Your opportunity to inspire thousands of learners to achieve dreams and academic pursue exellence'
|
||||
subText: 'Your opportunity to inspire thousands of learners to achieve dreams and academic pursue exellence'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -79,22 +76,12 @@
|
||||
<title>Teach | ClassroomIO</title>
|
||||
<meta property="og:image" itemprop="image" content="https://brand.cdn.clsrio.com/og/teach.png" />
|
||||
<meta property="og:title" content="Teach on ClassroomIO" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="On ClassroomIO you can make side income while doing what you love."
|
||||
/>
|
||||
<meta property="og:description" content="On ClassroomIO you can make side income while doing what you love." />
|
||||
|
||||
<meta
|
||||
property="og:image:secure_url"
|
||||
itemprop="image"
|
||||
content="https://brand.cdn.clsrio.com/og/teach.png"
|
||||
/>
|
||||
<meta property="og:image:secure_url" itemprop="image" content="https://brand.cdn.clsrio.com/og/teach.png" />
|
||||
|
||||
<meta name="twitter:title" content="Teach on ClassroomIO" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="On ClassroomIO you can make side income while doing what you love."
|
||||
/>
|
||||
<meta name="twitter:description" content="On ClassroomIO you can make side income while doing what you love." />
|
||||
<meta name="twitter:image" content="https://brand.cdn.clsrio.com/og/teach.png" />
|
||||
</svelte:head>
|
||||
|
||||
@@ -102,17 +89,16 @@
|
||||
<PageHeader className="flex items-center justify-evenly px-5 flex-wrap text-left">
|
||||
<div>
|
||||
<h1 class="text-5xl font-bold">Teach with us</h1>
|
||||
<p class="my-5 lg:w-[77%] lg:text-left text-center">
|
||||
<p class="my-5 text-center lg:w-[77%] lg:text-left">
|
||||
With ClassroomIO, you can make side income while teaching the subject you love
|
||||
</p>
|
||||
|
||||
<a
|
||||
<Button
|
||||
href="/teach/register"
|
||||
target="_blank"
|
||||
class="w-full block text-center lg:w-[30%] py-3 text-base text-white rounded-md bg-blue-700"
|
||||
>
|
||||
Register
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class=" ">
|
||||
@@ -120,21 +106,21 @@
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div class="mt-10 mx-auto">
|
||||
<div class="px-[10%] pb-10 border-b border-slate-100">
|
||||
<h1 class="text-4xl pb-28 text-center font-bold flex gap-3 justify-center items-center">
|
||||
<div class="mx-auto mt-10">
|
||||
<div class="border-b border-slate-100 px-[10%] pb-10">
|
||||
<h1 class="flex items-center justify-center gap-3 pb-28 text-center text-4xl font-bold">
|
||||
Why you should teach with us? <img src="/teach/star.svg" alt="" />
|
||||
</h1>
|
||||
|
||||
<div class="flex flex-wrap justify-center lg:justify-between mt-10">
|
||||
<div class="mt-10 flex flex-wrap justify-center lg:justify-between">
|
||||
{#each whyStart as why}
|
||||
<figure
|
||||
class="flex gap-3 flex-col justify-center items-center px-2 w-full h-full max-w-[250px] max-h-[380px] text-center"
|
||||
class="flex h-full max-h-[380px] w-full max-w-[250px] flex-col items-center justify-center gap-3 px-2 text-center"
|
||||
>
|
||||
<img src={why.src} alt="" class="w-[80px] h-[80px]" />
|
||||
<img src={why.src} alt="" class="h-[80px] w-[80px]" />
|
||||
<figcaption>
|
||||
<h1 class="font-bold text-base">{why.header}</h1>
|
||||
<p class="text-sm mt-1">
|
||||
<h1 class="text-base font-bold">{why.header}</h1>
|
||||
<p class="mt-1 text-sm">
|
||||
{why.subText}
|
||||
</p>
|
||||
</figcaption>
|
||||
@@ -143,66 +129,66 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-[10%] mt-10 py-10">
|
||||
<h1 class="text-4xl mb-28 text-center font-bold flex gap-3 justify-center items-center">
|
||||
<div class="mt-10 px-[10%] py-10">
|
||||
<h1 class="mb-28 flex items-center justify-center gap-3 text-center text-4xl font-bold">
|
||||
How to get started <img src="/teach/thumbs-up.svg" alt="" />
|
||||
</h1>
|
||||
|
||||
<div class="flex flex-wrap justify-between gap-y-10 mx-auto mt-5">
|
||||
<div class="mx-auto mt-5 flex flex-wrap justify-between gap-y-10">
|
||||
{#each steps as step, i}
|
||||
<div class="lg:max-w-[40%] px-10 py-5">
|
||||
<h1 class="font-bold text-2xl">
|
||||
<span class="px-2 py-1 mr-3 bg-[#F7F7F7] rounded-md">{i + 1}</span>
|
||||
<div class="px-10 py-5 lg:max-w-[40%]">
|
||||
<h1 class="text-2xl font-bold">
|
||||
<span class="mr-3 rounded-md bg-[#F7F7F7] px-2 py-1">{i + 1}</span>
|
||||
{step.title}
|
||||
</h1>
|
||||
<p class="text-base text-gray-600 mt-5">
|
||||
<p class="mt-5 text-base text-gray-600">
|
||||
{step.subText}
|
||||
</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex justify-center px-[5%] mt-3">
|
||||
<a
|
||||
<div class="mt-3 flex justify-center px-[5%]">
|
||||
<Button
|
||||
href="/teach/register"
|
||||
target="_blank"
|
||||
class="w-full lg:max-w-[20%] py-3 text-base text-white rounded-md bg-blue-700 text-center"
|
||||
rel="noopener noreferrer nofollow"
|
||||
>
|
||||
Get Started
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-2 lg:px-[12%] bg-[#F5F8FE] my-10 py-12 h-[75%] lg:h-[80%]">
|
||||
<h1 class="text-2xl text-center font-bold flex gap-3 justify-center items-center">
|
||||
<div class="my-10 h-[75%] bg-[#F5F8FE] px-2 py-12 lg:h-[80%] lg:px-[12%]">
|
||||
<h1 class="flex items-center justify-center gap-3 text-center text-2xl font-bold">
|
||||
Word from our fufilled facilitators
|
||||
</h1>
|
||||
|
||||
<div
|
||||
class="mt-10 w-full md:w-2/4 mx-auto border shadow-sm bg-white rounded-md text-center py-6 relative before:w-[4.5%] before:absolute before:left-0 before:top-0 before:h-full before:bg-[#0233BD] before:rounded-tl-md before:rounded-bl-md"
|
||||
class="relative mx-auto mt-10 w-full rounded-md border bg-white py-6 text-center shadow-sm before:absolute before:left-0 before:top-0 before:h-full before:w-[4.5%] before:rounded-bl-md before:rounded-tl-md before:bg-[#0233BD] md:w-2/4"
|
||||
>
|
||||
<h1 class="text-base font-medium leading-5 w-[77%] mx-auto">
|
||||
With ClassroomIO, I am training over 100 students. I am making more money than expected
|
||||
with little effort and my students are happy. Love the product
|
||||
<h1 class="mx-auto w-[77%] text-base font-medium leading-5">
|
||||
With ClassroomIO, I am training over 100 students. I am making more money than expected with little effort and
|
||||
my students are happy. Love the product
|
||||
</h1>
|
||||
<div class="flex flex-row gap-4 justify-center items-center mt-3">
|
||||
<div class="mt-3 flex flex-row items-center justify-center gap-4">
|
||||
<img src="/teach/user-profile.png" alt="" class="w-8" />
|
||||
<p class="text-sm">
|
||||
<span class="font-semibold mr-2"> Bilik Diana </span> English tutor
|
||||
<span class="mr-2 font-semibold"> Bilik Diana </span> English tutor
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-10 lg:px-[15%] lg:max-w-[65%] my-10 mt-10 mx-auto">
|
||||
<h1 class="text-2xl text-center font-bold">Frequently asked questions</h1>
|
||||
<div class="mx-auto my-10 mt-10 px-10 lg:max-w-[65%] lg:px-[15%]">
|
||||
<h1 class="text-center text-2xl font-bold">Frequently asked questions</h1>
|
||||
|
||||
<div class="mt-10">
|
||||
{#each faqs as faq, index}
|
||||
<div class="border-b border-black lg:px-3 pt-1 pb-2">
|
||||
<div class="border-b border-black pb-2 pt-1 lg:px-3">
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full font-medium text-sm md:text-lg my-2 flex justify-between text-left md:text-center"
|
||||
class="my-2 flex w-full justify-between text-left text-sm font-medium md:text-center md:text-lg"
|
||||
on:click={() => toggleAnswer(index)}
|
||||
>
|
||||
{faq.question} <span>{faq.showAnswer ? '-' : '+'}</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import PageSignupCTA from '$lib/PageSignupCTA/index.svelte';
|
||||
import { PageSignupCTA } from '$lib/components';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import PageHeader from '$lib/PageHeader/PageHeader.svelte';
|
||||
import { PageHeader } from '$lib/components';
|
||||
import { tools } from '$lib/utils/constants/tools';
|
||||
|
||||
const freeTools = tools.filter((item) => item.show);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import ToolsHeader from '$lib/ToolsHeader/ToolsHeader.svelte';
|
||||
import { ToolsHeader } from '$lib/components';
|
||||
|
||||
let namesInput = '';
|
||||
let numNames = 0;
|
||||
@@ -103,22 +103,14 @@
|
||||
|
||||
<svelte:head>
|
||||
<title>Random Name Picker | ClassroomIO</title>
|
||||
<meta
|
||||
property="og:image"
|
||||
itemprop="image"
|
||||
content="https://brand.cdn.clsrio.com/og/free-tools.png"
|
||||
/>
|
||||
<meta property="og:image" itemprop="image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
<meta property="og:title" content="Random Name Picker | ClassroomIO" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Use this online name picker to draw a random name from a list of names for your online or physical classroom."
|
||||
/>
|
||||
|
||||
<meta
|
||||
property="og:image:secure_url"
|
||||
itemprop="image"
|
||||
content="https://brand.cdn.clsrio.com/og/free-tools.png"
|
||||
/>
|
||||
<meta property="og:image:secure_url" itemprop="image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
|
||||
<meta name="twitter:title" content="Random Name Picker | ClassroomIO" />
|
||||
<meta
|
||||
@@ -128,41 +120,37 @@
|
||||
<meta name="twitter:image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
</svelte:head>
|
||||
|
||||
<section class="mt-[10%] md:mt-16 px-5 md:px-0 bg-white">
|
||||
<section class="bg-white px-5 md:px-0">
|
||||
<ToolsHeader>
|
||||
<img
|
||||
src="/free-tools/name-picker.svg"
|
||||
class="w-[15%] md:w-[5%] mx-auto border rounded-full"
|
||||
alt=""
|
||||
/>
|
||||
<h1 class="text-4xl md:text-6xl font-bold text-[#040F2D] my-3">Random Name Picker</h1>
|
||||
<p class="text-md text-[#656565] font-light md:font-normal md:w-[45%] mx-auto">
|
||||
Use this online name picker to draw a random name from a list of names, or to draw several
|
||||
names randomly out of a list. You can use it as a name randomizer for a class activities.
|
||||
<img src="/free-tools/name-picker.svg" class="mx-auto w-[15%] rounded-full border md:w-[5%]" alt="" />
|
||||
<h1 class="my-3 text-4xl font-bold text-[#040F2D] md:text-6xl">Random Name Picker</h1>
|
||||
<p class="text-md mx-auto font-light text-[#656565] md:w-[45%] md:font-normal">
|
||||
Use this online name picker to draw a random name from a list of names, or to draw several names randomly out of a
|
||||
list. You can use it as a name randomizer for a class activities.
|
||||
</p>
|
||||
</ToolsHeader>
|
||||
|
||||
<div class="border rounded-md md:w-[60%] my-10 py-8 md:py-[3%] mx-auto shadow-md bg-white">
|
||||
<div class="w-[85%] md:w-[70%] mx-auto bg-white">
|
||||
<h1 class="font-bold text-sm">List of names</h1>
|
||||
<div class="mx-auto my-10 rounded-md border bg-white py-8 shadow-md md:w-[60%] md:py-[3%]">
|
||||
<div class="mx-auto w-[85%] bg-white md:w-[70%]">
|
||||
<h1 class="text-sm font-bold">List of names</h1>
|
||||
|
||||
<!-- container -->
|
||||
<div
|
||||
class="relative focus-within:border-[#0233BD] border-2 mt-5 h-[12rem] pl-3 pt-2 overflow-hidden rounded-sm bg-[#F1F2F4]"
|
||||
class="relative mt-5 h-[12rem] overflow-hidden rounded-sm border-2 bg-[#F1F2F4] pl-3 pt-2 focus-within:border-[#0233BD]"
|
||||
>
|
||||
<!-- sidebar -->
|
||||
<div class="flex flex-col gap-y-3 absolute right-2 md:right-7 top-3">
|
||||
<div class="absolute right-2 top-3 flex flex-col gap-y-3 md:right-7">
|
||||
<button
|
||||
type="button"
|
||||
on:click={sortRandom}
|
||||
class="p-1.5 rounded-full bg-[#D9E0F5] flex justify-center items-center"
|
||||
class="flex items-center justify-center rounded-full bg-[#D9E0F5] p-1.5"
|
||||
>
|
||||
<img src="/sort-icon.svg" class="w-2.5" alt="" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
on:click={unsortRandom}
|
||||
class="p-1.5 rounded-full bg-[#D9E0F5] flex justify-center items-center"
|
||||
class="flex items-center justify-center rounded-full bg-[#D9E0F5] p-1.5"
|
||||
>
|
||||
<img src="/unsort-icon.svg" class="w-2.5" alt="" />
|
||||
</button>
|
||||
@@ -175,48 +163,46 @@
|
||||
tabindex={0}
|
||||
contenteditable
|
||||
bind:textContent={namesInput}
|
||||
class="w-full pr-9 md:pr-14 overflow-y-auto text-xs block border-0 outline-none h-full"
|
||||
class="block h-full w-full overflow-y-auto border-0 pr-9 text-xs outline-none md:pr-14"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<!-- word count -->
|
||||
<p
|
||||
class="rounded-xl w-[80px] mt-1 py-1 bg-[#D9E0F5] text-center text-[#0F62FE] text-[10px] font-semibold"
|
||||
>
|
||||
<p class="mt-1 w-[80px] rounded-xl bg-[#D9E0F5] py-1 text-center text-[10px] font-semibold text-[#0F62FE]">
|
||||
name count: {wordCount}
|
||||
</p>
|
||||
</div>
|
||||
<p class="py-4 border-b text-xs">Each name must be separated by a comma</p>
|
||||
<p class="border-b py-4 text-xs">Each name must be separated by a comma</p>
|
||||
|
||||
<div class="mt-10">
|
||||
<h1 class="font-bold text-sm">Number of Names</h1>
|
||||
<h1 class="text-sm font-bold">Number of Names</h1>
|
||||
<!-- number input & button -->
|
||||
<div class="flex flex-wrap gap-y-3 justify-between w-full mt-5">
|
||||
<div class="mt-5 flex w-full flex-wrap justify-between gap-y-3">
|
||||
<input
|
||||
type="number"
|
||||
bind:value={numNames}
|
||||
class="px-3 py-2 text-sm w-full md:w-2/4 border-2 rounded-sm outline focus:border-[#0233BD] bg-[#F1F2F4] outline-none"
|
||||
class="w-full rounded-sm border-2 bg-[#F1F2F4] px-3 py-2 text-sm outline-none outline focus:border-[#0233BD] md:w-2/4"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
on:click={selectName}
|
||||
class="bg-[#0F62FE] rounded-md text-sm py-3 md:py-0 text-white w-[70%] md:w-[35%]"
|
||||
class="w-[70%] rounded-md bg-[#0F62FE] py-3 text-sm text-white md:w-[35%] md:py-0"
|
||||
>Select name(s) at random</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- footer logo -->
|
||||
<div class="py-3 mt-10 mb-5 bg-[#F1F6FF] flex justify-center items-center gap-3">
|
||||
<div class="mb-5 mt-10 flex items-center justify-center gap-3 bg-[#F1F6FF] py-3">
|
||||
<img src="/free-tools/name-picker.svg" class="w-[12%] md:w-[9%]" alt="" />
|
||||
{#if selectedNames.length > 0}
|
||||
<h1 class="text-[#0233BD] text-xl font-bold">Selected Names</h1>
|
||||
<h1 class="text-xl font-bold text-[#0233BD]">Selected Names</h1>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- loading state -->
|
||||
{#if selectedNames.length > 0 && !avatarUrlsFetched}
|
||||
<div class="w-full flex justify-center items-center">
|
||||
<div class="flex w-full items-center justify-center">
|
||||
<p class="font-bold">Loading...</p>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -226,10 +212,10 @@
|
||||
<div>
|
||||
<ul>
|
||||
{#each selectedNames as name, index}
|
||||
<li class="py-2 border-b">
|
||||
<div class="flex item-center gap-3 md:gap-5 justify-center">
|
||||
<li class="border-b py-2">
|
||||
<div class="item-center flex justify-center gap-3 md:gap-5">
|
||||
<img src={avatarUrls[index]} alt="" class="w-7" />
|
||||
<p class="font-medium text-sm md:text-base flex justify-center items-center">
|
||||
<p class="flex items-center justify-center text-sm font-medium md:text-base">
|
||||
{name}
|
||||
</p>
|
||||
</div>
|
||||
@@ -237,17 +223,16 @@
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<div class="flex flex-wrap justify-between md:justify-center items-center md:gap-3 mt-3">
|
||||
<div class="mt-3 flex flex-wrap items-center justify-between md:justify-center md:gap-3">
|
||||
<button
|
||||
type="button"
|
||||
on:click={resetEntry}
|
||||
class="py-2 border text-xs md:text-sm rounded-md w-[45%] md:w-[20%]"
|
||||
>Reset Entries</button
|
||||
class="w-[45%] rounded-md border py-2 text-xs md:w-[20%] md:text-sm">Reset Entries</button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
on:click={selectName}
|
||||
class="py-2 bg-[#0F62FE] rounded-md text-xs md:text-sm text-white w-[45%] md:w-[27%]"
|
||||
class="w-[45%] rounded-md bg-[#0F62FE] py-2 text-xs text-white md:w-[27%] md:text-sm"
|
||||
>Select other names</button
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import ToolsHeader from '$lib/ToolsHeader/ToolsHeader.svelte';
|
||||
import { ToolsHeader } from '$lib/components';
|
||||
|
||||
interface TodoItem {
|
||||
id: number;
|
||||
@@ -167,15 +167,15 @@
|
||||
<meta name="twitter:image" content="" />
|
||||
</svelte:head>
|
||||
|
||||
<section class="mt-[10%] md:mt-16 px-1 md:px-0 w-full md:w-full bg-white">
|
||||
<section class=" w-full bg-white px-1 md:w-full md:px-0">
|
||||
<ToolsHeader>
|
||||
<img
|
||||
src="/free-tools/pomodoro.svg"
|
||||
class="w-[15%] md:w-[5%] mx-auto border rounded-full"
|
||||
class="mx-auto w-[15%] rounded-full border md:w-[5%]"
|
||||
alt=""
|
||||
/>
|
||||
<h1 class="text-4xl md:text-6xl font-bold text-[#040F2D] my-3">Pomodoro Timer</h1>
|
||||
<p class="text-sm mt-10 text-[#656565] font-light md:font-normal md:w-[45%] mx-auto">
|
||||
<h1 class="my-3 text-4xl font-bold text-[#040F2D] md:text-6xl">Pomodoro Timer</h1>
|
||||
<p class="mx-auto mt-10 text-sm font-light text-[#656565] md:w-[45%] md:font-normal">
|
||||
Make your workday more engaging and effective with the Pomodoro timer, break work into
|
||||
25-minute focused intervals called "pomodoros," followed by 5-minute breaks. take a longer
|
||||
break.
|
||||
@@ -183,20 +183,20 @@
|
||||
</ToolsHeader>
|
||||
|
||||
<div
|
||||
class="body w-full md:h-[60vh] flex flex-col gap-y-10 md:flex-row justify-evenly px-5 md:px-10 my-10 text-white text-center"
|
||||
class="body my-10 flex w-full flex-col justify-evenly gap-y-10 px-5 text-center text-white md:h-[60vh] md:flex-row md:px-10"
|
||||
>
|
||||
<!-- left side -->
|
||||
<div
|
||||
class="w-full md:w-[50%] bg-[#0D4CFF] rounded-lg md:rounded-2xl py-7 px-5 md:p-10 flex flex-col gap-y-16 md:gap-y-0 justify-between"
|
||||
class="flex w-full flex-col justify-between gap-y-16 rounded-lg bg-[#0D4CFF] px-5 py-7 md:w-[50%] md:gap-y-0 md:rounded-2xl md:p-10"
|
||||
>
|
||||
<!-- types -->
|
||||
<div class="flex item-center justify-evenly">
|
||||
<div class="item-center flex justify-evenly">
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => setTimerState('pomodoro')}
|
||||
class:bg-[#3ADFECED]={timerState === 'pomodoro'}
|
||||
class:bg-[#0233BD]={timerState !== 'pomodoro'}
|
||||
class="transition-all duration-500 italic py-1.5 md:py-2 w-[25%] text-xs md:text-sm md:font-medium rounded-md hover:bg-[#3ADFECED]"
|
||||
class="w-[25%] rounded-md py-1.5 text-xs italic transition-all duration-500 hover:bg-[#3ADFECED] md:py-2 md:text-sm md:font-medium"
|
||||
>Pomodoro</button
|
||||
>
|
||||
<button
|
||||
@@ -204,7 +204,7 @@
|
||||
on:click={() => setTimerState('long-break')}
|
||||
class:bg-[#3ADFECED]={timerState === 'long-break'}
|
||||
class:bg-[#0233BD]={timerState !== 'long-break'}
|
||||
class="transition-all duration-500 italic py-1.5 md:py-2 w-[25%] text-xs md:text-sm md:font-medium rounded-md hover:bg-[#3ADFECED]"
|
||||
class="w-[25%] rounded-md py-1.5 text-xs italic transition-all duration-500 hover:bg-[#3ADFECED] md:py-2 md:text-sm md:font-medium"
|
||||
>Short Break
|
||||
</button>
|
||||
<button
|
||||
@@ -212,13 +212,13 @@
|
||||
on:click={() => setTimerState('short-break')}
|
||||
class:bg-[#3ADFECED]={timerState === 'short-break'}
|
||||
class:bg-[#0233BD]={timerState !== 'short-break'}
|
||||
class="transition-all duration-500 italic py-1.5 md:py-2 w-[25%] text-xs md:text-sm md:font-medium rounded-md hover:bg-[#3ADFECED]"
|
||||
class="w-[25%] rounded-md py-1.5 text-xs italic transition-all duration-500 hover:bg-[#3ADFECED] md:py-2 md:text-sm md:font-medium"
|
||||
>Long Break
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- countdown -->
|
||||
<h1 class="text-7xl md:text-9xl font-bold">{countdownDisplay}</h1>
|
||||
<h1 class="text-7xl font-bold md:text-9xl">{countdownDisplay}</h1>
|
||||
|
||||
<!-- controls -->
|
||||
<div class="flex items-center justify-center gap-7">
|
||||
@@ -227,7 +227,7 @@
|
||||
<img
|
||||
src="/free-tools/pomodoro/restart-icon.svg"
|
||||
alt="Restart Icon"
|
||||
class="w-7 hover:scale-110 transition-all duration-300"
|
||||
class="w-7 transition-all duration-300 hover:scale-110"
|
||||
/>
|
||||
</button>
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
<button
|
||||
type="button"
|
||||
on:click={isPaused ? startCountdown : pauseCountdown}
|
||||
class="bg-white text-[#0542CC] border text-base font-bold uppercase py-3 px-14 rounded-md hover:bg-transparent hover:text-white hover:border-white hover:border transition-all duration-300"
|
||||
class="rounded-md border bg-white px-14 py-3 text-base font-bold uppercase text-[#0542CC] transition-all duration-300 hover:border hover:border-white hover:bg-transparent hover:text-white"
|
||||
>
|
||||
{isPaused ? 'Start' : 'Pause'}
|
||||
</button>
|
||||
@@ -245,16 +245,16 @@
|
||||
<img
|
||||
src="/free-tools/pomodoro/timer-play-icon.svg"
|
||||
alt="Play Icon"
|
||||
class="w-7 hover:scale-110 transition-all duration-300"
|
||||
class="w-7 transition-all duration-300 hover:scale-110"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- right side -->
|
||||
<div class="w-full md:w-[36%] relative">
|
||||
<div class="relative w-full md:w-[36%]">
|
||||
<h1
|
||||
class="bg-[#040F2D] uppercase text-xl font-bold py-4"
|
||||
class="bg-[#040F2D] py-4 text-xl font-bold uppercase"
|
||||
style="clip-path: polygon(0 0, 100% 1%, 100% 65%, 0% 100%);"
|
||||
>
|
||||
To do list
|
||||
@@ -262,28 +262,28 @@
|
||||
|
||||
<div class="overflow-hidden">
|
||||
<!-- todos -->
|
||||
<div class="overflow-y-auto mt-3 max-h-[40vh] md:max-h-[50vh]">
|
||||
<div class="mt-3 max-h-[40vh] overflow-y-auto md:max-h-[50vh]">
|
||||
{#each todoList as todo, i}
|
||||
<div class="border text-black p-5">
|
||||
<div class="border p-5 text-black">
|
||||
{#if todo.isEditing}
|
||||
<form on:submit|preventDefault={() => setEditing(i, false)}>
|
||||
<p class="text-xs text-left text-[#656565] font-semibold">Pomodoro name</p>
|
||||
<p class="text-left text-xs font-semibold text-[#656565]">Pomodoro name</p>
|
||||
<input
|
||||
type="text"
|
||||
bind:this={addInput}
|
||||
bind:value={todo.content}
|
||||
class="bg-[#F1F2F4] border-[#EAEAEA] w-full border mt-2 px-3 py-2 text-base outline-none"
|
||||
class="mt-2 w-full border border-[#EAEAEA] bg-[#F1F2F4] px-3 py-2 text-base outline-none"
|
||||
/>
|
||||
<div class="flex justify-end items-center gap-5 mt-5">
|
||||
<div class="mt-5 flex items-center justify-end gap-5">
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => deleteTodo(i)}
|
||||
class="rounded-sm py-2 px-5 text-xs font-semibold bg-[#F7F7F7]"
|
||||
class="rounded-sm bg-[#F7F7F7] px-5 py-2 text-xs font-semibold"
|
||||
>Discard</button
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="rounded-sm py-2 px-5 text-xs font-semibold bg-[#0D4CFF] text-white"
|
||||
class="rounded-sm bg-[#0D4CFF] px-5 py-2 text-xs font-semibold text-white"
|
||||
>Save</button
|
||||
>
|
||||
</div>
|
||||
@@ -294,7 +294,7 @@
|
||||
<p class="text-sm font-medium">{todo.content}</p>
|
||||
|
||||
<!-- pen and menu icon -->
|
||||
<div class="relative flex justify-between w-[15%]">
|
||||
<div class="relative flex w-[15%] justify-between">
|
||||
<button type="button" on:click={() => setEditing(i, true)}>
|
||||
<img src="/free-tools/pomodoro/pen-icon.svg" alt="Pen icon" class="w-5" />
|
||||
</button>
|
||||
@@ -305,19 +305,19 @@
|
||||
|
||||
{#if todo.isVisible}
|
||||
<ul
|
||||
class="z-40 absolute top-6 -right-3 text-xs w-[8rem] bg-white border shadow-md rounded-[5px]"
|
||||
class="absolute -right-3 top-6 z-40 w-[8rem] rounded-[5px] border bg-white text-xs shadow-md"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => markTodoAsDone(i)}
|
||||
class="w-full font-medium text-left py-3 px-5"
|
||||
class="w-full px-5 py-3 text-left font-medium"
|
||||
>
|
||||
Mark as done
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => deleteTodo(i)}
|
||||
class="w-full font-medium text-left py-3 px-5 bg-red-600 text-white rounded-b-[5px]"
|
||||
class="w-full rounded-b-[5px] bg-red-600 px-5 py-3 text-left font-medium text-white"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
@@ -326,9 +326,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center mt-10">
|
||||
<div class="mt-10 flex items-center justify-between">
|
||||
<p
|
||||
class="rounded-[3px] text-xs font-medium text-[#656565] bg-[#EAEAEA] py-0.5 px-1.5"
|
||||
class="rounded-[3px] bg-[#EAEAEA] px-1.5 py-0.5 text-xs font-medium text-[#656565]"
|
||||
>
|
||||
{todo.time}
|
||||
</p>
|
||||
@@ -345,12 +345,12 @@
|
||||
todo.isPaused = !todo.isPaused;
|
||||
}}
|
||||
class="{todo.isDone
|
||||
? 'bg-[#00D06C] border-none text-white'
|
||||
? 'border-none bg-[#00D06C] text-white'
|
||||
: todo.isPaused
|
||||
? 'bg-white text-[#0D4CFF] border-[#0D4CFF]'
|
||||
: 'bg-[#0D4CFF] text-white border-none'}
|
||||
? 'border-[#0D4CFF] bg-white text-[#0D4CFF]'
|
||||
: 'border-none bg-[#0D4CFF] text-white'}
|
||||
|
||||
flex items-center border-2 justify-center gap-2 py-1.5 px-4 rounded-[4px] text-xs font-bold uppercase"
|
||||
flex items-center justify-center gap-2 rounded-[4px] border-2 px-4 py-1.5 text-xs font-bold uppercase"
|
||||
>
|
||||
{#if todo.isDone}
|
||||
<img src="/free-tools/pomodoro/done-icon.svg" alt="Done icon" />
|
||||
@@ -374,7 +374,7 @@
|
||||
<button
|
||||
type="button"
|
||||
on:click={addTodo}
|
||||
class="bg-[#1D4ED8] rounded-md text-center font-medium mt-10 py-3 w-full flex justify-center items-center gap-3"
|
||||
class="mt-10 flex w-full items-center justify-center gap-3 rounded-md bg-[#1D4ED8] py-3 text-center font-medium"
|
||||
>
|
||||
Add new item
|
||||
<img src="/free-tools/pomodoro/add-icon.svg" alt="Add icon" class="w-3" />
|
||||
@@ -385,9 +385,9 @@
|
||||
</section>
|
||||
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:[email protected]&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Geist:[email protected]&display=swap');
|
||||
|
||||
.body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-family: 'Geist', sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { LogoFacebook, LogoLinkedin } from 'carbon-icons-svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { fly } from 'svelte/transition';
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
import Mood from './components/Mood.svelte';
|
||||
import ToolsHeader from '$lib/ToolsHeader/ToolsHeader.svelte';
|
||||
import Avatar from './components/Avatar.svelte';
|
||||
import Background from './components/Background.svelte';
|
||||
import Report from './components/Report.svelte';
|
||||
import FullView from './components/FullView.svelte';
|
||||
import DownloadButton from './components/DownloadButton.svelte';
|
||||
import { htmlBody, type HtmlBody, openModal, isFormComplete } from './components/store';
|
||||
import Facebook from '@lucide/svelte/icons/facebook';
|
||||
import Linkedin from '@lucide/svelte/icons/linkedin';
|
||||
|
||||
import { htmlBody, type HtmlBody, openModal, isFormComplete } from '$lib/features/tools/progress/store';
|
||||
|
||||
import Mood from '$lib/features/tools/progress/mood.svelte';
|
||||
import Avatar from '$lib/features/tools/progress/avatar.svelte';
|
||||
import Report from '$lib/features/tools/progress/report.svelte';
|
||||
import FullView from '$lib/features/tools/progress/full-view.svelte';
|
||||
import Background from '$lib/features/tools/progress/background.svelte';
|
||||
import { ToolsHeader } from '$lib/components';
|
||||
import DownloadButton from '$lib/features/tools/progress/download-button.svelte';
|
||||
|
||||
const MAX_CHARS = 160;
|
||||
let remainingChars = MAX_CHARS;
|
||||
@@ -65,22 +68,14 @@
|
||||
|
||||
<svelte:head>
|
||||
<title>Random Name Picker | ClassroomIO</title>
|
||||
<meta
|
||||
property="og:image"
|
||||
itemprop="image"
|
||||
content="https://brand.cdn.clsrio.com/og/free-tools.png"
|
||||
/>
|
||||
<meta property="og:image" itemprop="image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
<meta property="og:title" content="Random Name Picker | ClassroomIO" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Use this online name picker to draw a random name from a list of names for your online or physical classroom."
|
||||
/>
|
||||
|
||||
<meta
|
||||
property="og:image:secure_url"
|
||||
itemprop="image"
|
||||
content="https://brand.cdn.clsrio.com/og/free-tools.png"
|
||||
/>
|
||||
<meta property="og:image:secure_url" itemprop="image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
|
||||
<meta name="twitter:title" content="Random Name Picker | ClassroomIO" />
|
||||
<meta
|
||||
@@ -90,17 +85,12 @@
|
||||
<meta name="twitter:image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
</svelte:head>
|
||||
|
||||
<section class="mt-[10%] md:mt-16 px-1 md:px-0 w-full md:w-full">
|
||||
<section class=" w-full px-1 md:w-full md:px-0">
|
||||
<ToolsHeader>
|
||||
<img
|
||||
src="/free-tools/progress-report.svg"
|
||||
class="w-[15%] md:w-[5%] mx-auto border rounded-full"
|
||||
alt=""
|
||||
/>
|
||||
<h1 class="text-4xl md:text-6xl font-bold text-[#040F2D] my-3">Progress Report</h1>
|
||||
<p class="text-md text-[#656565] font-light md:font-normal md:w-[45%] mx-auto">
|
||||
Generate cool reports of your learning progress. Share reports with your network for
|
||||
collaborative learning
|
||||
<img src="/free-tools/progress-report.svg" class="mx-auto w-[15%] rounded-full border md:w-[5%]" alt="" />
|
||||
<h1 class="my-3 text-4xl font-bold text-[#040F2D] md:text-6xl">Progress Report</h1>
|
||||
<p class="text-md mx-auto font-light text-[#656565] md:w-[45%] md:font-normal">
|
||||
Generate cool reports of your learning progress. Share reports with your network for collaborative learning
|
||||
</p>
|
||||
</ToolsHeader>
|
||||
|
||||
@@ -111,29 +101,25 @@
|
||||
<FullView />
|
||||
|
||||
<div
|
||||
class="border rounded-md w-full md:w-11/12 lg:w-[80%] my-10 mx-auto shadow-md flex md:flex-row flex-col justify-evenly"
|
||||
class="mx-auto my-10 flex w-full flex-col justify-evenly rounded-md border shadow-md md:w-11/12 md:flex-row lg:w-[80%]"
|
||||
>
|
||||
<!-- left side -->
|
||||
{#if showSetter}
|
||||
<div
|
||||
transition:fly={{ y: 100, easing: sineInOut }}
|
||||
class="w-full mx-auto md:w-[48%] md:max-w-[500px] p-5 md:border-r"
|
||||
class="mx-auto w-full p-5 md:w-[48%] md:max-w-[500px] md:border-r"
|
||||
>
|
||||
<!-- preview button -->
|
||||
<div class="flex md:hidden justify-end mb-5">
|
||||
<div class="mb-5 flex justify-end md:hidden">
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showSetter = false;
|
||||
showReport = true;
|
||||
}}
|
||||
class="flex justify-between items-center gap-2 px-3 py-1 rounded-full text-white text-xs bg-[#0233BD]"
|
||||
class="flex items-center justify-between gap-2 rounded-full bg-[#0233BD] px-3 py-1 text-xs text-white"
|
||||
>
|
||||
<img
|
||||
src="/free-tools/progress-report/preview-icon.svg"
|
||||
alt="preview icon"
|
||||
class="w-4"
|
||||
/>
|
||||
<img src="/free-tools/progress-report/preview-icon.svg" alt="preview icon" class="w-4" />
|
||||
Preview
|
||||
</button>
|
||||
</div>
|
||||
@@ -146,7 +132,7 @@
|
||||
bind:value={$htmlBody.name}
|
||||
on:input={(event) => handleInputChange(event, 'name')}
|
||||
placeholder="Enter your name here"
|
||||
class="w-full border my-3 py-2 px-3 outline-none rounded-sm bg-[#F1F2F4] text-xs placeholder:text-sm placeholder:text-[#ADADAD]"
|
||||
class="my-3 w-full rounded-sm border bg-[#F1F2F4] px-3 py-2 text-xs outline-none placeholder:text-sm placeholder:text-[#ADADAD]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -156,18 +142,17 @@
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => ($openModal.mood = true)}
|
||||
class="w-full flex justify-between items-center border my-3 py-2 px-3 outline-none rounded-sm bg-[#F1F2F4] text-gray-400 text-sm"
|
||||
class="my-3 flex w-full items-center justify-between rounded-sm border bg-[#F1F2F4] px-3 py-2 text-sm text-gray-400 outline-none"
|
||||
>
|
||||
{#if $htmlBody.mood.text}
|
||||
<div
|
||||
class="px-3 py-1 border border-[#EDEDED] text-white bg-blue-500 rounded-2xl font-semibold text-[7px] md:text-xs flex items-center justify-between"
|
||||
class="flex items-center justify-between rounded-2xl border border-[#EDEDED] bg-blue-500 px-3 py-1 text-[7px] font-semibold text-white md:text-xs"
|
||||
>
|
||||
{#if $htmlBody.mood.text}
|
||||
<span class="flex gap-1 items-center">
|
||||
<p class="font-semibold text-[9px] md:text-xs">is {$htmlBody.mood.text}</p>
|
||||
<span class="flex items-center gap-1">
|
||||
<p class="text-[9px] font-semibold md:text-xs">is {$htmlBody.mood.text}</p>
|
||||
<img
|
||||
src="https://assets.cdn.clsrio.com/progress-report/emojis/{$htmlBody.mood
|
||||
.iconSrc}.png"
|
||||
src="https://assets.cdn.clsrio.com/progress-report/emojis/{$htmlBody.mood.iconSrc}.png"
|
||||
alt={$htmlBody.mood.text}
|
||||
class="w-3"
|
||||
/>
|
||||
@@ -176,11 +161,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
What feeling describes you?
|
||||
<img
|
||||
src="/free-tools/progress-report/smiley-face-icon.svg"
|
||||
alt="smiley face"
|
||||
class="w-3"
|
||||
/>
|
||||
<img src="/free-tools/progress-report/smiley-face-icon.svg" alt="smiley face" class="w-3" />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
@@ -193,15 +174,15 @@
|
||||
maxlength={MAX_CHARS}
|
||||
on:input={(event) => handleInputChange(event, 'learning')}
|
||||
placeholder="Tell us what you are learning"
|
||||
class="w-full h-[17vh] border my-3 py-2 px-3 outline-none rounded-sm bg-[#F1F2F4] text-sm placeholder:text-sm placeholder:text-[#ADADAD]"
|
||||
class="my-3 h-[17vh] w-full rounded-sm border bg-[#F1F2F4] px-3 py-2 text-sm outline-none placeholder:text-sm placeholder:text-[#ADADAD]"
|
||||
></textarea>
|
||||
<p class="text-xs text-right text-[#656565]">{remainingChars} characters remaining</p>
|
||||
<p class="text-right text-xs text-[#656565]">{remainingChars} characters remaining</p>
|
||||
</div>
|
||||
|
||||
<!-- range input -->
|
||||
<div class="mt-3">
|
||||
<p class="text-sm text-[#656565] pb-4">Estimate your progress</p>
|
||||
<div class="flex justify-between items-center">
|
||||
<p class="pb-4 text-sm text-[#656565]">Estimate your progress</p>
|
||||
<div class="flex items-center justify-between">
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
@@ -216,18 +197,16 @@
|
||||
</div>
|
||||
|
||||
<!-- avatar button -->
|
||||
<div class="flex flex-wrap justify-between items-center mt-3">
|
||||
<div class="mt-3 flex flex-wrap items-center justify-between">
|
||||
<div class="w-full md:w-2/4">
|
||||
<p class="text-[12px] md:text-sm text-[#656565]">Choose your avatar</p>
|
||||
<p class="text-[12px] text-[#656565] md:text-sm">Choose your avatar</p>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => ($openModal.avatar = true)}
|
||||
class="bg-[#F7F7F7] w-full py-1.5 px-5 md:px-0 mt-3 flex md:w-[65%] items-center justify-between md:justify-center gap-2"
|
||||
class="mt-3 flex w-full items-center justify-between gap-2 bg-[#F7F7F7] px-5 py-1.5 md:w-[65%] md:justify-center md:px-0"
|
||||
>
|
||||
<img
|
||||
src={`https://assets.cdn.clsrio.com/progress-report/avatars/${
|
||||
$htmlBody.avatar || 'avatar_m'
|
||||
}.svg`}
|
||||
src={`https://assets.cdn.clsrio.com/progress-report/avatars/${$htmlBody.avatar || 'avatar_m'}.svg`}
|
||||
alt=""
|
||||
class="w-[15%] md:w-[30%]"
|
||||
/>
|
||||
@@ -241,11 +220,11 @@
|
||||
|
||||
<!-- & background -->
|
||||
<div class="w-full md:w-2/4">
|
||||
<p class="text-[12px] md:text-sm text-[#656565]">Choose your background</p>
|
||||
<p class="text-[12px] text-[#656565] md:text-sm">Choose your background</p>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => ($openModal.background = true)}
|
||||
class="bg-[#F7F7F7] w-full py-1.5 px-5 md:px-0 mt-3 flex md:w-[65%] items-center justify-between md:justify-center gap-2"
|
||||
class="mt-3 flex w-full items-center justify-between gap-2 bg-[#F7F7F7] px-5 py-1.5 md:w-[65%] md:justify-center md:px-0"
|
||||
>
|
||||
<img
|
||||
src={`https://assets.cdn.clsrio.com/progress-report/backgrounds/${
|
||||
@@ -270,41 +249,35 @@
|
||||
|
||||
<div
|
||||
transition:fly={{ y: 100, easing: sineInOut }}
|
||||
class="w-full mx-auto md:w-[48%] md:min-w-[400px] md:max-w-[500px] px-5 md:px-1 md:mt-0 mt-5 md:p-5"
|
||||
class="mx-auto mt-5 w-full px-5 md:mt-0 md:w-[48%] md:min-w-[400px] md:max-w-[500px] md:p-5 md:px-1"
|
||||
>
|
||||
<div class="hidden md:block">
|
||||
<Report />
|
||||
|
||||
<!-- download & share button -->
|
||||
<div class="mt-9 pt-8 px-2 h-auto border-t">
|
||||
<div class="mt-9 h-auto border-t px-2 pt-8">
|
||||
<DownloadButton {isDisabled} {isDownloading} text="Download Image" />
|
||||
|
||||
<!-- share button -->
|
||||
<div class="w-full md:w-[70%] mx-auto my-5">
|
||||
<h1 class="text-sm font-semibold text-center">Share on image on social media:</h1>
|
||||
<div class="mx-auto my-5 w-full md:w-[70%]">
|
||||
<h1 class="text-center text-sm font-semibold">Share on image on social media:</h1>
|
||||
|
||||
<div
|
||||
class="flex justify-evenly border rounded-xl gap-5 w-[60%] md:w-[80%] px-5 py-2 mt-2 mx-auto"
|
||||
>
|
||||
<div class="mx-auto mt-2 flex w-[60%] justify-evenly gap-5 rounded-xl border px-5 py-2 md:w-[80%]">
|
||||
<button
|
||||
type="button"
|
||||
on:click={shareOnFacebook}
|
||||
class="w-10 hover:scale-[1.2] transition-all duration-300"
|
||||
class="w-10 transition-all duration-300 hover:scale-[1.2]"
|
||||
>
|
||||
<LogoFacebook size={32} />
|
||||
<Facebook size={32} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
on:click={shareOnLinkedIn}
|
||||
class="w-10 hover:scale-[1.2] transition-all duration-300"
|
||||
class="w-10 transition-all duration-300 hover:scale-[1.2]"
|
||||
>
|
||||
<LogoLinkedin size={32} />
|
||||
<Linkedin size={32} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
on:click={shareOnTwitter}
|
||||
class="w-5 hover:scale-[1.2] transition-all duration-300"
|
||||
>
|
||||
<button type="button" on:click={shareOnTwitter} class="w-5 transition-all duration-300 hover:scale-[1.2]">
|
||||
<img src="/twitter_logo.png" alt="X.com" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -314,14 +287,14 @@
|
||||
|
||||
{#if showReport}
|
||||
<!-- hide button -->
|
||||
<div class="flex justify-end mb-5">
|
||||
<div class="mb-5 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showReport = false;
|
||||
showSetter = true;
|
||||
}}
|
||||
class="flex justify-between items-center gap-2 px-3 py-1 rounded-full text-white text-xs bg-[#0233BD]"
|
||||
class="flex items-center justify-between gap-2 rounded-full bg-[#0233BD] px-3 py-1 text-xs text-white"
|
||||
>
|
||||
<img src="/free-tools/progress-report/hide-icon.svg" alt="preview icon" class="w-4" />
|
||||
Close
|
||||
@@ -330,35 +303,29 @@
|
||||
<Report />
|
||||
|
||||
<!-- download & share button -->
|
||||
<div class="mt-9 pt-8 px-2 h-auto border-t">
|
||||
<div class="mt-9 h-auto border-t px-2 pt-8">
|
||||
<DownloadButton {isDisabled} {isDownloading} />
|
||||
|
||||
<!-- share button -->
|
||||
<div class="w-full md:w-[70%] mx-auto my-5">
|
||||
<h1 class="text-sm font-semibold text-center">Share on image on social media:</h1>
|
||||
<div class="mx-auto my-5 w-full md:w-[70%]">
|
||||
<h1 class="text-center text-sm font-semibold">Share on image on social media:</h1>
|
||||
|
||||
<div
|
||||
class="flex justify-evenly border rounded-xl gap-5 w-[60%] md:w-[80%] px-5 py-2 mt-2 mx-auto"
|
||||
>
|
||||
<div class="mx-auto mt-2 flex w-[60%] justify-evenly gap-5 rounded-xl border px-5 py-2 md:w-[80%]">
|
||||
<button
|
||||
type="button"
|
||||
on:click={shareOnFacebook}
|
||||
class="w-10 hover:scale-[1.2] transition-all duration-300"
|
||||
class="w-10 transition-all duration-300 hover:scale-[1.2]"
|
||||
>
|
||||
<LogoFacebook size={32} />
|
||||
<Facebook size={32} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
on:click={shareOnLinkedIn}
|
||||
class="w-10 hover:scale-[1.2] transition-all duration-300"
|
||||
class="w-10 transition-all duration-300 hover:scale-[1.2]"
|
||||
>
|
||||
<LogoLinkedin size={32} />
|
||||
<Linkedin size={32} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
on:click={shareOnTwitter}
|
||||
class="w-5 hover:scale-[1.2] transition-all duration-300"
|
||||
>
|
||||
<button type="button" on:click={shareOnTwitter} class="w-5 transition-all duration-300 hover:scale-[1.2]">
|
||||
<img src="/twitter_logo.png" alt="X.com" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { openModal, htmlBody } from './store';
|
||||
import Modal from '$lib/Modal/Modal.svelte';
|
||||
|
||||
interface Avatar {
|
||||
src: string;
|
||||
}
|
||||
|
||||
let isAddIcon = false;
|
||||
let hoveredIndex: number | null = null;
|
||||
|
||||
let avatars: Avatar[] = [
|
||||
{ src: 'avatar_a' },
|
||||
{ src: 'avatar_b' },
|
||||
{ src: 'avatar_c' },
|
||||
{ src: 'avatar_d' },
|
||||
{ src: 'avatar_e' },
|
||||
{ src: 'avatar_f' },
|
||||
{ src: 'avatar_g' },
|
||||
{ src: 'avatar_h' },
|
||||
{ src: 'avatar_i' },
|
||||
{ src: 'avatar_j' },
|
||||
{ src: 'avatar_k' },
|
||||
{ src: 'avatar_l' },
|
||||
{ src: 'avatar_m' },
|
||||
{ src: 'avatar_n' },
|
||||
{ src: 'avatar_o' },
|
||||
{ src: 'avatar_p' },
|
||||
{ src: 'avatar_q' },
|
||||
{ src: 'avatar_r' },
|
||||
{ src: 'avatar_s' },
|
||||
{ src: 'avatar_t' }
|
||||
];
|
||||
|
||||
function selectAvatar(avatar: Avatar) {
|
||||
$htmlBody.avatar = avatar.src;
|
||||
$openModal.avatar = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal bind:open={$openModal.avatar}>
|
||||
<!-- heading -->
|
||||
<div class="flex justify-between">
|
||||
<h1 class="text-sm font-semibold">Choose your Avatars</h1>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => ($openModal.avatar = false)}
|
||||
class="p-2 bg-[#F1F6FF] hover:scale-110 transition-all duration-300 rounded-full w-6"
|
||||
>
|
||||
<img src="/free-tools/progress-report/close-icon.svg" alt="Close icon" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- map for each avatar -->
|
||||
<div class="flex flex-wrap justify-evenly gap-y-3 mt-3">
|
||||
{#each avatars as avatar, index}
|
||||
<button
|
||||
on:click={() => {
|
||||
selectAvatar(avatar);
|
||||
}}
|
||||
on:mouseenter={() => {
|
||||
hoveredIndex = index;
|
||||
isAddIcon = true;
|
||||
}}
|
||||
on:mouseleave={() => {
|
||||
hoveredIndex = null;
|
||||
isAddIcon = false;
|
||||
}}
|
||||
class="relative w-[17%] flex justify-center items-center shadow-sm rounded-full hover:scale-110 transition-all duration-300"
|
||||
>
|
||||
<!-- + icon overlay -->
|
||||
{#if isAddIcon && hoveredIndex === index}
|
||||
<div
|
||||
class="w-full h-full flex justify-center items-center bg-white border absolute rounded-full opacity-[0.7] blur-md"
|
||||
></div>
|
||||
<img src="/free-tools/progress-report/hover-plus-icon.svg" alt="" class="absolute" />
|
||||
{/if}
|
||||
<!-- avatar -->
|
||||
<img
|
||||
src="https://assets.cdn.clsrio.com/progress-report/avatar/{avatar.src}.svg"
|
||||
alt=""
|
||||
class="w-full"
|
||||
/>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
<!-- -->
|
||||
</Modal>
|
||||
@@ -1,87 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Modal from '$lib/Modal/Modal.svelte';
|
||||
import { htmlBody, openModal } from './store';
|
||||
|
||||
interface Background {
|
||||
src: string;
|
||||
}
|
||||
|
||||
let backgrounds: Background[] = [
|
||||
{
|
||||
src: 'blue_tetiary_background'
|
||||
},
|
||||
{
|
||||
src: 'blue_secondary_background'
|
||||
},
|
||||
{
|
||||
src: 'blue_primary_background'
|
||||
},
|
||||
{
|
||||
src: 'purple_tetiary_background'
|
||||
},
|
||||
{
|
||||
src: 'purple_secondary_background'
|
||||
},
|
||||
{
|
||||
src: 'purple_primary_background'
|
||||
},
|
||||
{
|
||||
src: 'black_tetiary_background'
|
||||
},
|
||||
{
|
||||
src: 'black_secondary_background'
|
||||
},
|
||||
{
|
||||
src: 'black_primary_background'
|
||||
},
|
||||
{
|
||||
src: 'yellow_tetiary_background'
|
||||
},
|
||||
{
|
||||
src: 'yellow_secondary_background'
|
||||
},
|
||||
{
|
||||
src: 'yellow_primary_background'
|
||||
}
|
||||
];
|
||||
|
||||
function selectBackground(background: Background) {
|
||||
$htmlBody.background = background.src;
|
||||
$openModal.background = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal className="h-full max-h-[60vh]" bind:open={$openModal.background}>
|
||||
<!-- -->
|
||||
<div class="flex justify-between">
|
||||
<h1 class="text-sm font-semibold">Choose your Background</h1>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => ($openModal.background = false)}
|
||||
class="p-2 bg-[#F1F6FF] hover:scale-110 transition-all duration-300 rounded-full w-6"
|
||||
>
|
||||
<img src="/free-tools/progress-report/close-icon.svg" alt="Close icon" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- -->
|
||||
<div
|
||||
class="flex flex-wrap justify-between gap-y-3 mt-3 overflow-y-auto overflow-x-hidden h-[85%]"
|
||||
>
|
||||
{#each backgrounds as background}
|
||||
<button
|
||||
on:click={() => {
|
||||
selectBackground(background);
|
||||
}}
|
||||
class="flex w-[30%] items-center shadow-sm hover:scale-110 transition-all duration-300"
|
||||
>
|
||||
<img
|
||||
src="https://assets.cdn.clsrio.com/progress-report/backgrounds/{background.src}.webp"
|
||||
alt=""
|
||||
class=""
|
||||
/>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
<!-- -->
|
||||
</Modal>
|
||||
@@ -1,11 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Modal from '$lib/Modal/Modal.svelte';
|
||||
import Report from './Report.svelte';
|
||||
import { openModal } from './store';
|
||||
</script>
|
||||
|
||||
<Modal bind:open={$openModal.fullscreen}>
|
||||
<!-- -->
|
||||
<Report />
|
||||
<!-- -->
|
||||
</Modal>
|
||||
@@ -2,8 +2,8 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { fly } from 'svelte/transition';
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
import Timer from './components/Timer.svelte';
|
||||
import ToolsHeader from '$lib/ToolsHeader/ToolsHeader.svelte';
|
||||
import Timer from '$lib/features/tools/stopwatch/timer.svelte';
|
||||
import { ToolsHeader } from '$lib/components';
|
||||
|
||||
let timer: NodeJS.Timeout;
|
||||
let buzzSound: HTMLAudioElement;
|
||||
@@ -66,9 +66,7 @@
|
||||
const hrs = Math.floor(totalSeconds / 3600);
|
||||
const mins = Math.floor((totalSeconds % 3600) / 60);
|
||||
const secs = totalSeconds % 60;
|
||||
displayTime = `${String(hrs).padStart(2, '0')}:${String(mins).padStart(2, '0')}:${String(
|
||||
secs
|
||||
).padStart(2, '0')}`;
|
||||
displayTime = `${String(hrs).padStart(2, '0')}:${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
|
||||
|
||||
if (formattedTime === '00:00') {
|
||||
formattedTime = `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
|
||||
@@ -117,22 +115,14 @@
|
||||
|
||||
<svelte:head>
|
||||
<title>Activity Stopwatch | ClassroomIO</title>
|
||||
<meta
|
||||
property="og:image"
|
||||
itemprop="image"
|
||||
content="https://brand.cdn.clsrio.com/og/free-tools.png"
|
||||
/>
|
||||
<meta property="og:image" itemprop="image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
<meta property="og:title" content="Activity Stopwatch | ClassroomIO" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Stay on track with a customizable stopwatch for timed tasks, quizzes, and study sessions"
|
||||
/>
|
||||
|
||||
<meta
|
||||
property="og:image:secure_url"
|
||||
itemprop="image"
|
||||
content="https://brand.cdn.clsrio.com/og/free-tools.png"
|
||||
/>
|
||||
<meta property="og:image:secure_url" itemprop="image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
|
||||
<meta name="twitter:title" content="Activity Stopwatch | ClassroomIO" />
|
||||
<meta
|
||||
@@ -142,76 +132,72 @@
|
||||
<meta name="twitter:image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
</svelte:head>
|
||||
|
||||
<section class="mt-[10%] md:mt-16 px-5 md:px-0 bg-white">
|
||||
<section class=" bg-white px-5 md:px-0">
|
||||
<ToolsHeader>
|
||||
<img
|
||||
src="/free-tools/name-picker.svg"
|
||||
class="w-[15%] md:w-[5%] mx-auto border rounded-full"
|
||||
alt=""
|
||||
/>
|
||||
<h1 class="text-4xl md:text-6xl font-bold text-[#040F2D] my-3">Activity Stopwatch</h1>
|
||||
<p class="text-md text-[#656565] font-light md:font-normal md:w-[45%] mx-auto">
|
||||
Stay on track and enhance productivity with our customizable stopwatch for timed tasks,
|
||||
quizzes, and study sessions.
|
||||
<img src="/free-tools/name-picker.svg" class="mx-auto w-[15%] rounded-full border md:w-[5%]" alt="" />
|
||||
<h1 class="my-3 text-4xl font-bold text-[#040F2D] md:text-6xl">Activity Stopwatch</h1>
|
||||
<p class="text-md mx-auto font-light text-[#656565] md:w-[45%] md:font-normal">
|
||||
Stay on track and enhance productivity with our customizable stopwatch for timed tasks, quizzes, and study
|
||||
sessions.
|
||||
</p>
|
||||
</ToolsHeader>
|
||||
|
||||
<div class="w-full md:w-2/4 my-10 mx-auto border rounded-md overflow-hidden bg-white">
|
||||
<div class="mx-auto my-10 w-full overflow-hidden rounded-md border bg-white md:w-2/4">
|
||||
<!-- countdown setter -->
|
||||
{#if !isNextStep}
|
||||
<div transition:fly={{ y: -300, delay: 0, easing: sineInOut }} class="px-6 py-8 bg-white">
|
||||
<div transition:fly={{ y: -300, delay: 0, easing: sineInOut }} class="bg-white px-6 py-8">
|
||||
<form on:submit|preventDefault={startTimer}>
|
||||
<div>
|
||||
<h1 class="font-bold text-sm">Name of activity</h1>
|
||||
<h1 class="text-sm font-bold">Name of activity</h1>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={activityName}
|
||||
class="w-full rounded-sm border py-2 mt-3 bg-[#F1F2F4] outline-none px-3 focus-within:border-[#0233BD] focus-within:border"
|
||||
class="mt-3 w-full rounded-sm border bg-[#F1F2F4] px-3 py-2 outline-none focus-within:border focus-within:border-[#0233BD]"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-10 bg-white">
|
||||
<h1 class="font-bold text-sm">Set an Activity</h1>
|
||||
<div class="flex flex-wrap justify-between gap-y-5 w-full mt-3 uppercase font-bold">
|
||||
<div class="bg-[#F1F2F4] w-full md:w-[30%] border px-4 py-5">
|
||||
<div class="w-2/4 md:w-full mx-auto flex items-end gap-2">
|
||||
<h1 class="text-sm font-bold">Set an Activity</h1>
|
||||
<div class="mt-3 flex w-full flex-wrap justify-between gap-y-5 font-bold uppercase">
|
||||
<div class="w-full border bg-[#F1F2F4] px-4 py-5 md:w-[30%]">
|
||||
<div class="mx-auto flex w-2/4 items-end gap-2 md:w-full">
|
||||
<input
|
||||
type="number"
|
||||
bind:value={hours}
|
||||
class="w-[80%] md:w-[75%] outline-none text-center py-3 px-1.5 text-3xl placeholder:text-3xl placeholder:text-black"
|
||||
class="w-[80%] px-1.5 py-3 text-center text-3xl outline-none placeholder:text-3xl placeholder:text-black md:w-[75%]"
|
||||
/>
|
||||
<p class="text-[10px]">hrs</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-[#F1F2F4] w-full md:w-[30%] border px-4 py-5">
|
||||
<div class="w-2/4 md:w-full mx-auto flex items-end gap-2">
|
||||
<div class="w-full border bg-[#F1F2F4] px-4 py-5 md:w-[30%]">
|
||||
<div class="mx-auto flex w-2/4 items-end gap-2 md:w-full">
|
||||
<input
|
||||
type="number"
|
||||
bind:value={minutes}
|
||||
class="w-[80%] md:w-[75%] outline-none text-center py-3 px-1.5 text-3xl placeholder:text-3xl placeholder:text-black"
|
||||
class="w-[80%] px-1.5 py-3 text-center text-3xl outline-none placeholder:text-3xl placeholder:text-black md:w-[75%]"
|
||||
/>
|
||||
<p class="text-[10px]">mins</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-[#F1F2F4] w-full md:w-[30%] border px-4 py-5">
|
||||
<div class="w-2/4 md:w-full mx-auto flex items-end gap-2">
|
||||
<div class="w-full border bg-[#F1F2F4] px-4 py-5 md:w-[30%]">
|
||||
<div class="mx-auto flex w-2/4 items-end gap-2 md:w-full">
|
||||
<input
|
||||
type="number"
|
||||
bind:value={seconds}
|
||||
class="w-[80%] md:w-[75%] outline-none text-center py-3 px-1.5 text-3xl placeholder:text-3xl placeholder:text-black"
|
||||
class="w-[80%] px-1.5 py-3 text-center text-3xl outline-none placeholder:text-3xl placeholder:text-black md:w-[75%]"
|
||||
/>
|
||||
<p class="text-[10px]">sec</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-10">
|
||||
<div class="mt-10 flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
class="bg-[#0F62FE] text-white text-sm px-5 py-3 rounded-md flex gap-3 items-center"
|
||||
class="flex items-center gap-3 rounded-md bg-[#0F62FE] px-5 py-3 text-sm text-white"
|
||||
>
|
||||
<img src="/free-tools/stopwatch/loading-timer.svg" alt="" />
|
||||
Start Timer</button
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
import Board from './components/Board.svelte';
|
||||
import { TicTacToe } from './components/board';
|
||||
import { scores, changeToComputer } from './components/store';
|
||||
import ToolsHeader from '$lib/ToolsHeader/ToolsHeader.svelte';
|
||||
import Board from '$lib/features/tools/tic-tac-toe/board.svelte';
|
||||
import { TicTacToe } from '$lib/features/tools/tic-tac-toe/board';
|
||||
import { scores, changeToComputer } from '$lib/features/tools/tic-tac-toe/store';
|
||||
import { ToolsHeader } from '$lib/components';
|
||||
|
||||
let startGame = false;
|
||||
let message: string | null = null;
|
||||
@@ -78,22 +78,14 @@
|
||||
|
||||
<svelte:head>
|
||||
<title>Tic Tac Toe | ClassroomIO</title>
|
||||
<meta
|
||||
property="og:image"
|
||||
itemprop="image"
|
||||
content="https://brand.cdn.clsrio.com/og/free-tools.png"
|
||||
/>
|
||||
<meta property="og:image" itemprop="image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
<meta property="og:title" content="Tic Tac Toe | ClassroomIO" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="A simple and free tic tac toe game to play to your friends in class or outside the classroom."
|
||||
/>
|
||||
|
||||
<meta
|
||||
property="og:image:secure_url"
|
||||
itemprop="image"
|
||||
content="https://brand.cdn.clsrio.com/og/free-tools.png"
|
||||
/>
|
||||
<meta property="og:image:secure_url" itemprop="image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
|
||||
<meta name="twitter:title" content="Tic Tac Toe | ClassroomIO" />
|
||||
<meta
|
||||
@@ -103,42 +95,37 @@
|
||||
<meta name="twitter:image" content="https://brand.cdn.clsrio.com/og/free-tools.png" />
|
||||
</svelte:head>
|
||||
|
||||
<section class="mt-[10%] md:mt-16 px-5 md:px-0 bg-white">
|
||||
<section class=" bg-white px-5 md:px-0">
|
||||
<ToolsHeader className="mb-10">
|
||||
<img
|
||||
src="/free-tools/tic-tac.svg"
|
||||
class="w-14 md:w-[5%] mx-auto border rounded-full"
|
||||
alt="Tic Tac Icon"
|
||||
/>
|
||||
<h1 class="text-4xl md:text-6xl font-bold text-[#040F2D] my-3">Tic-Tac game</h1>
|
||||
<p class="text-md text-[#656565] font-light md:font-normal md:w-[45%] mx-auto">
|
||||
More than just a game; it's an educational tool that teaches pattern recognition, and
|
||||
decision-making.
|
||||
<img src="/free-tools/tic-tac.svg" class="mx-auto w-14 rounded-full border md:w-[5%]" alt="Tic Tac Icon" />
|
||||
<h1 class="my-3 text-4xl font-bold text-[#040F2D] md:text-6xl">Tic-Tac game</h1>
|
||||
<p class="text-md mx-auto font-light text-[#656565] md:w-[45%] md:font-normal">
|
||||
More than just a game; it's an educational tool that teaches pattern recognition, and decision-making.
|
||||
</p>
|
||||
</ToolsHeader>
|
||||
|
||||
<div
|
||||
class="overflow-hidden relative tic-tac-bg text-white w-full h-[55vh] md:w-[59%] md:h-[82vh] border-8 border-black mx-auto flex justify-center items-center bg-white"
|
||||
class="tic-tac-bg relative mx-auto flex h-[55vh] w-full items-center justify-center overflow-hidden border-8 border-black bg-white text-white md:h-[82vh] md:w-[59%]"
|
||||
>
|
||||
<!-- congratulatory message -->
|
||||
{#if message}
|
||||
<div
|
||||
transition:fly={{ x: -100, delay: 600, easing: sineInOut }}
|
||||
class="absolute w-full top-[43%] flex flex-col justify-center items-center z-[150]"
|
||||
class="absolute top-[43%] z-[150] flex w-full flex-col items-center justify-center"
|
||||
>
|
||||
<div
|
||||
class="flex gap-10 justify-center py-1 h-[8vh] md:h-[13vh] w-full items-center bg-white font-bold text-[#0233BD]"
|
||||
class="flex h-[8vh] w-full items-center justify-center gap-10 bg-white py-1 font-bold text-[#0233BD] md:h-[13vh]"
|
||||
>
|
||||
{#if message != 'Tie game!'}
|
||||
<img src="/free-tools/tic-tac/winner-star-icon.svg" alt="Star icon" class="h-full" />
|
||||
{/if}
|
||||
<h2 class="md:text-base text-xs">{message}</h2>
|
||||
<h2 class="text-xs md:text-base">{message}</h2>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
on:click={playAgain}
|
||||
class="px-16 pt-1 pb-1.5 md:text-base text-xs border border-white font-bold rounded-md mt-10 bg-[#0F62FE]"
|
||||
class="mt-10 rounded-md border border-white bg-[#0F62FE] px-16 pb-1.5 pt-1 text-xs font-bold md:text-base"
|
||||
>Play Again</button
|
||||
>
|
||||
</div>
|
||||
@@ -146,17 +133,13 @@
|
||||
|
||||
<!-- this condition renders the game -->
|
||||
{#if startGame}
|
||||
<div class="w-full h-full p-5 flex flex-col border justify-between items-center">
|
||||
<div class="flex h-full w-full flex-col items-center justify-between border p-5">
|
||||
<header class="flex w-full items-center justify-between">
|
||||
<button
|
||||
on:click={changePlayer}
|
||||
class="text-[10px] md:text-xs w-[35%] gap-2 md:w-28 flex flex-row items-center leading-3 font-medium"
|
||||
class="flex w-[35%] flex-row items-center gap-2 text-[10px] font-medium leading-3 md:w-28 md:text-xs"
|
||||
>
|
||||
<img
|
||||
src="/free-tools/tic-tac/computer-icon.svg"
|
||||
alt="Computer icon"
|
||||
class="w-4 md:w-6"
|
||||
/>
|
||||
<img src="/free-tools/tic-tac/computer-icon.svg" alt="Computer icon" class="w-4 md:w-6" />
|
||||
{#if $changeToComputer.change}
|
||||
Play two players
|
||||
{:else}
|
||||
@@ -165,7 +148,7 @@
|
||||
</button>
|
||||
<button
|
||||
on:click={newGame}
|
||||
class="text-[10px] w-24 flex flex-col gap-1 p-2 shadow-md rounded-md items-center leading-3 font-medium border text-[#0542CC] bg-white"
|
||||
class="flex w-24 flex-col items-center gap-1 rounded-md border bg-white p-2 text-[10px] font-medium leading-3 text-[#0542CC] shadow-md"
|
||||
>
|
||||
<img src="/free-tools/tic-tac/restart-icon.svg" alt="Restart icon" class="w-3 md:w-4" />
|
||||
Reset Score</button
|
||||
@@ -176,19 +159,17 @@
|
||||
<Board {game} {winner} />
|
||||
|
||||
<!-- scores -->
|
||||
<div
|
||||
class="flex justify-between md:justify-center items-center gap-10 w-full text-white font-semibold"
|
||||
>
|
||||
<div class="md:text-3xl text-2xl flex items-center md:gap-5 gap-2">
|
||||
<span class="px-1 md:p-1 bg-[#FE28CB99] md:text-sm text-[10px]">Player 1</span>
|
||||
<div class="flex w-full items-center justify-between gap-10 font-semibold text-white md:justify-center">
|
||||
<div class="flex items-center gap-2 text-2xl md:gap-5 md:text-3xl">
|
||||
<span class="bg-[#FE28CB99] px-1 text-[10px] md:p-1 md:text-sm">Player 1</span>
|
||||
{$scores.X}
|
||||
</div>
|
||||
<div class="md:text-3xl text-2xl flex items-center md:gap-5 gap-2">
|
||||
<span class="px-1 md:p-1 bg-[#0F62FE] md:text-sm text-[10px]">Draw</span>
|
||||
<div class="flex items-center gap-2 text-2xl md:gap-5 md:text-3xl">
|
||||
<span class="bg-[#0F62FE] px-1 text-[10px] md:p-1 md:text-sm">Draw</span>
|
||||
{$scores.draw}
|
||||
</div>
|
||||
<div class="md:text-3xl text-2xl flex items-center md:gap-5 gap-2">
|
||||
<span class="px-1 md:p-1 bg-[#94F7FFB2] md:text-sm text-[10px]">Player 2</span>
|
||||
<div class="flex items-center gap-2 text-2xl md:gap-5 md:text-3xl">
|
||||
<span class="bg-[#94F7FFB2] px-1 text-[10px] md:p-1 md:text-sm">Player 2</span>
|
||||
{$scores.O}
|
||||
</div>
|
||||
</div>
|
||||
@@ -197,11 +178,7 @@
|
||||
<!-- this condition renders the homepage -->
|
||||
{:else}
|
||||
<div class="flex flex-col items-center gap-5">
|
||||
<img
|
||||
src="/free-tools/tic-tac/tic-tac-start-icon.svg"
|
||||
alt="Tic tac toe game icon"
|
||||
class="w-[50%] md:w-[70%]"
|
||||
/>
|
||||
<img src="/free-tools/tic-tac/tic-tac-start-icon.svg" alt="Tic tac toe game icon" class="w-[50%] md:w-[70%]" />
|
||||
<div class="flex flex-col gap-3">
|
||||
<button
|
||||
type="button"
|
||||
@@ -209,7 +186,7 @@
|
||||
changePlayer();
|
||||
startTicTac();
|
||||
}}
|
||||
class="mt-5 px-[3.3rem] pt-1 pb-1.5 border border-white text-sm font-bold rounded-md bg-[#0F62FE]"
|
||||
class="mt-5 rounded-md border border-white bg-[#0F62FE] px-[3.3rem] pb-1.5 pt-1 text-sm font-bold"
|
||||
>
|
||||
Play with computer</button
|
||||
>
|
||||
@@ -218,7 +195,7 @@
|
||||
on:click={() => {
|
||||
startTicTac();
|
||||
}}
|
||||
class="px-16 pt-1 pb-1.5 border border-white text-sm font-bold rounded-md bg-[#0F62FE]"
|
||||
class="rounded-md border border-white bg-[#0F62FE] px-16 pb-1.5 pt-1 text-sm font-bold"
|
||||
>Play two players</button
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import sequence from 'svelte-sequential-preprocessor';
|
||||
import adapter from '@sveltejs/adapter-auto';
|
||||
import adapterNode from '@sveltejs/adapter-auto';
|
||||
import adapterVercel from '@sveltejs/adapter-vercel';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { mdsvex, escapeSvelte } from 'mdsvex';
|
||||
import path from 'path';
|
||||
import shiki from 'shiki';
|
||||
import remarkUnwrapImages from 'remark-unwrap-images';
|
||||
import remarkToc from 'remark-toc';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
|
||||
const useNodeAdapter = process.env.PUBLIC_IS_SELFHOSTED === 'true';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config}*/
|
||||
|
||||
/** @type {import('mdsvex').MdsvexOptions} */
|
||||
@@ -30,9 +33,18 @@ const mdsvexOptions = {
|
||||
|
||||
const config = {
|
||||
kit: {
|
||||
adapter: adapter()
|
||||
adapter: useNodeAdapter ? adapterNode() : adapterVercel(),
|
||||
alias: {
|
||||
$lib: path.resolve('./src/lib'),
|
||||
'$src/tools': path.resolve('./node_modules/@cio/ui/src/tools/index.ts'),
|
||||
'$src/base/*': path.resolve('./node_modules/@cio/ui/src/base/*'),
|
||||
'@cio/ui': path.resolve('./node_modules/@cio/ui/src'),
|
||||
'@cio/ui/*': path.resolve('./node_modules/@cio/ui/src/*'),
|
||||
'@cio/utils': path.resolve('./node_modules/@cio/utils/dist'),
|
||||
'@cio/utils/*': path.resolve('./node_modules/@cio/utils/dist/*')
|
||||
}
|
||||
},
|
||||
extensions: ['.svelte', '.md'],
|
||||
preprocess: sequence([vitePreprocess(), mdsvex(mdsvexOptions)])
|
||||
preprocess: [vitePreprocess(), mdsvex(mdsvexOptions)]
|
||||
};
|
||||
export default config;
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
@@ -2,11 +2,24 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
silenceDeprecations: ['legacy-js-api']
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [sveltekit()],
|
||||
build: {
|
||||
sourcemap: true
|
||||
},
|
||||
optimizeDeps: {
|
||||
entries: ['src/routes/**/+*.{js,ts,svelte}']
|
||||
},
|
||||
ssr: {
|
||||
noExternal: ['bits-ui', 'svelte-toolbelt', '@lucide/svelte', 'runed']
|
||||
},
|
||||
resolve: {
|
||||
mainFields: ['browser']
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const FIELDS = ['value', 'onValueChange', 'dir', 'orientation', 'viewport', 'disabled'] as string[];
|
||||
@@ -0,0 +1,155 @@
|
||||
<script module>
|
||||
import { defineMeta } from '@storybook/addon-svelte-csf';
|
||||
import * as NavigationMenu from '@cio/ui/base/navigation-menu';
|
||||
import { FIELDS } from './fields';
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: 'Molecules/NavigationMenu',
|
||||
component: NavigationMenu.Root,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
controls: {
|
||||
include: FIELDS
|
||||
}
|
||||
},
|
||||
tags: ['autodocs']
|
||||
});
|
||||
</script>
|
||||
|
||||
<Story name="Default">
|
||||
{#snippet template()}
|
||||
<NavigationMenu.Root class="ui:relative">
|
||||
<NavigationMenu.List class="ui:flex ui:gap-2">
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Trigger>Getting started</NavigationMenu.Trigger>
|
||||
<NavigationMenu.Content>
|
||||
<ul class="ui:grid ui:gap-3 ui:p-6 ui:md:w-[400px] ui:lg:w-[500px] ui:lg:grid-cols-[.75fr_1fr]">
|
||||
<li class="ui:row-span-3">
|
||||
<NavigationMenu.Link
|
||||
href="/"
|
||||
class="ui:from-muted/50 ui:to-muted ui:flex ui:h-full ui:w-full ui:select-none ui:flex-col ui:justify-end ui:rounded-md ui:bg-gradient-to-b ui:p-6 ui:no-underline ui:outline-none ui:focus:shadow-md"
|
||||
>
|
||||
<div class="ui:mb-2 ui:mt-4 ui:text-lg ui:font-medium">ClassroomIO</div>
|
||||
<p class="ui:text-muted-foreground ui:text-sm ui:leading-tight">
|
||||
A powerful learning management system for creating and managing online courses.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
<li>
|
||||
<NavigationMenu.Link
|
||||
href="/docs"
|
||||
class="ui:hover:bg-accent ui:hover:text-accent-foreground ui:focus:bg-accent ui:focus:text-accent-foreground ui:block ui:select-none ui:space-y-1 ui:rounded-md ui:p-3 ui:leading-none ui:no-underline ui:outline-none ui:transition-colors"
|
||||
>
|
||||
<div class="ui:text-sm ui:font-medium ui:leading-none">Introduction</div>
|
||||
<p class="ui:text-muted-foreground ui:line-clamp-2 ui:text-sm ui:leading-snug">
|
||||
Learn how to get started with ClassroomIO.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
<li>
|
||||
<NavigationMenu.Link
|
||||
href="/docs/installation"
|
||||
class="ui:hover:bg-accent ui:hover:text-accent-foreground ui:focus:bg-accent ui:focus:text-accent-foreground ui:block ui:select-none ui:space-y-1 ui:rounded-md ui:p-3 ui:leading-none ui:no-underline ui:outline-none ui:transition-colors"
|
||||
>
|
||||
<div class="ui:text-sm ui:font-medium ui:leading-none">Installation</div>
|
||||
<p class="ui:text-muted-foreground ui:line-clamp-2 ui:text-sm ui:leading-snug">
|
||||
How to install and configure your instance.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
<li>
|
||||
<NavigationMenu.Link
|
||||
href="/docs/components"
|
||||
class="ui:hover:bg-accent ui:hover:text-accent-foreground ui:focus:bg-accent ui:focus:text-accent-foreground ui:block ui:select-none ui:space-y-1 ui:rounded-md ui:p-3 ui:leading-none ui:no-underline ui:outline-none ui:transition-colors"
|
||||
>
|
||||
<div class="ui:text-sm ui:font-medium ui:leading-none">Components</div>
|
||||
<p class="ui:text-muted-foreground ui:line-clamp-2 ui:text-sm ui:leading-snug">
|
||||
Explore the available UI components.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
</ul>
|
||||
</NavigationMenu.Content>
|
||||
</NavigationMenu.Item>
|
||||
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Trigger>Features</NavigationMenu.Trigger>
|
||||
<NavigationMenu.Content>
|
||||
<ul class="ui:grid ui:w-[400px] ui:gap-3 ui:p-4 md:ui:w-[500px] md:ui:grid-cols-2 lg:ui:w-[600px]">
|
||||
<li>
|
||||
<NavigationMenu.Link
|
||||
href="/courses"
|
||||
class="ui:hover:bg-accent ui:hover:text-accent-foreground ui:focus:bg-accent ui:focus:text-accent-foreground ui:block ui:select-none ui:space-y-1 ui:rounded-md ui:p-3 ui:leading-none ui:no-underline ui:outline-none ui:transition-colors"
|
||||
>
|
||||
<div class="ui:text-sm ui:font-medium ui:leading-none">Course Management</div>
|
||||
<p class="ui:text-muted-foreground ui:line-clamp-2 ui:text-sm ui:leading-snug">
|
||||
Create and organize your courses with ease.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
<li>
|
||||
<NavigationMenu.Link
|
||||
href="/students"
|
||||
class="ui:hover:bg-accent ui:hover:text-accent-foreground ui:focus:bg-accent ui:focus:text-accent-foreground ui:block ui:select-none ui:space-y-1 ui:rounded-md ui:p-3 ui:leading-none ui:no-underline ui:outline-none ui:transition-colors"
|
||||
>
|
||||
<div class="ui:text-sm ui:font-medium ui:leading-none">Student Tracking</div>
|
||||
<p class="ui:text-muted-foreground ui:line-clamp-2 ui:text-sm ui:leading-snug">
|
||||
Monitor student progress and performance.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
<li>
|
||||
<NavigationMenu.Link
|
||||
href="/assessments"
|
||||
class="ui:hover:bg-accent ui:hover:text-accent-foreground ui:focus:bg-accent ui:focus:text-accent-foreground ui:block ui:select-none ui:space-y-1 ui:rounded-md ui:p-3 ui:leading-none ui:no-underline ui:outline-none ui:transition-colors"
|
||||
>
|
||||
<div class="ui:text-sm ui:font-medium ui:leading-none">Assessments</div>
|
||||
<p class="ui:text-muted-foreground ui:line-clamp-2 ui:text-sm ui:leading-snug">
|
||||
Create quizzes, assignments, and exams.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
<li>
|
||||
<NavigationMenu.Link
|
||||
href="/analytics"
|
||||
class="ui:hover:bg-accent ui:hover:text-accent-foreground ui:focus:bg-accent ui:focus:text-accent-foreground ui:block ui:select-none ui:space-y-1 ui:rounded-md ui:p-3 ui:leading-none ui:no-underline ui:outline-none ui:transition-colors"
|
||||
>
|
||||
<div class="ui:text-sm ui:font-medium ui:leading-none">Analytics</div>
|
||||
<p class="ui:text-muted-foreground ui:line-clamp-2 ui:text-sm ui:leading-snug">
|
||||
View detailed analytics and reports.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
<li>
|
||||
<NavigationMenu.Link
|
||||
href="/collaboration"
|
||||
class="ui:hover:bg-accent ui:hover:text-accent-foreground ui:focus:bg-accent ui:focus:text-accent-foreground ui:block ui:select-none ui:space-y-1 ui:rounded-md ui:p-3 ui:leading-none ui:no-underline ui:outline-none ui:transition-colors"
|
||||
>
|
||||
<div class="ui:text-sm ui:font-medium ui:leading-none">Collaboration</div>
|
||||
<p class="ui:text-muted-foreground ui:line-clamp-2 ui:text-sm ui:leading-snug">
|
||||
Enable team collaboration and discussions.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
<li>
|
||||
<NavigationMenu.Link
|
||||
href="/integrations"
|
||||
class="ui:hover:bg-accent ui:hover:text-accent-foreground ui:focus:bg-accent ui:focus:text-accent-foreground ui:block ui:select-none ui:space-y-1 ui:rounded-md ui:p-3 ui:leading-none ui:no-underline ui:outline-none ui:transition-colors"
|
||||
>
|
||||
<div class="ui:text-sm ui:font-medium ui:leading-none">Integrations</div>
|
||||
<p class="ui:text-muted-foreground ui:line-clamp-2 ui:text-sm ui:leading-snug">
|
||||
Connect with your favorite tools.
|
||||
</p>
|
||||
</NavigationMenu.Link>
|
||||
</li>
|
||||
</ul>
|
||||
</NavigationMenu.Content>
|
||||
</NavigationMenu.Item>
|
||||
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Link href="/docs">Documentation</NavigationMenu.Link>
|
||||
</NavigationMenu.Item>
|
||||
</NavigationMenu.List>
|
||||
</NavigationMenu.Root>
|
||||
{/snippet}
|
||||
</Story>
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@internationalized/date": "^3.8.1",
|
||||
"@lucide/svelte": "^0.554.0",
|
||||
"@lucide/svelte": "^0.544.0",
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.5.27",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
class="ui:ring-offset-background ui:focus:ring-ring ui:rounded-xs ui:focus:outline-hidden ui:absolute ui:top-4 ui:opacity-70 ui:transition-opacity ui:hover:opacity-100 ui:focus:ring-2 ui:focus:ring-offset-2 ui:disabled:pointer-events-none ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none ui:[&_svg]:shrink-0 ui:end-4 ui:cursor-pointer"
|
||||
>
|
||||
<XIcon />
|
||||
<span class="sr-only">Close</span>
|
||||
<span class="ui:sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
{/if}
|
||||
</DialogPrimitive.Content>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import Root from "./navigation-menu.svelte";
|
||||
import Content from "./navigation-menu-content.svelte";
|
||||
import Indicator from "./navigation-menu-indicator.svelte";
|
||||
import Item from "./navigation-menu-item.svelte";
|
||||
import Link from "./navigation-menu-link.svelte";
|
||||
import List from "./navigation-menu-list.svelte";
|
||||
import Trigger from "./navigation-menu-trigger.svelte";
|
||||
import Viewport from "./navigation-menu-viewport.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
Content,
|
||||
Indicator,
|
||||
Item,
|
||||
Link,
|
||||
List,
|
||||
Trigger,
|
||||
Viewport,
|
||||
//
|
||||
Root as NavigationMenuRoot,
|
||||
Content as NavigationMenuContent,
|
||||
Indicator as NavigationMenuIndicator,
|
||||
Item as NavigationMenuItem,
|
||||
Link as NavigationMenuLink,
|
||||
List as NavigationMenuList,
|
||||
Trigger as NavigationMenuTrigger,
|
||||
Viewport as NavigationMenuViewport,
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { NavigationMenu as NavigationMenuPrimitive } from 'bits-ui';
|
||||
import { cn } from '../../tools';
|
||||
|
||||
let { ref = $bindable(null), class: className, ...restProps }: NavigationMenuPrimitive.ContentProps = $props();
|
||||
</script>
|
||||
|
||||
<NavigationMenuPrimitive.Content
|
||||
bind:ref
|
||||
data-slot="navigation-menu-content"
|
||||
class={cn(
|
||||
'ui:data-[motion^=from-]:animate-in ui:data-[motion^=to-]:animate-out ui:data-[motion^=from-]:fade-in ui:data-[motion^=to-]:fade-out ui:data-[motion=from-end]:slide-in-from-end-52 ui:data-[motion=from-start]:slide-in-from-start-52 ui:data-[motion=to-end]:slide-out-to-end-52 ui:data-[motion=to-start]:slide-out-to-start-52 ui:start-0 ui:top-0 ui:w-full md:ui:absolute md:ui:w-auto',
|
||||
'ui:group-data-[viewport=false]/navigation-menu:bg-popover ui:group-data-[viewport=false]/navigation-menu:text-popover-foreground ui:group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in ui:group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out ui:group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 ui:group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 ui:group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 ui:group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 ui:**:data-[slot=navigation-menu-link]:focus:ring-0 ui:**:data-[slot=navigation-menu-link]:focus:outline-none ui:group-data-[viewport=false]/navigation-menu:top-full ui:group-data-[viewport=false]/navigation-menu:mt-1.5 ui:group-data-[viewport=false]/navigation-menu:overflow-hidden ui:group-data-[viewport=false]/navigation-menu:rounded-md ui:group-data-[viewport=false]/navigation-menu:border ui:group-data-[viewport=false]/navigation-menu:shadow ui:group-data-[viewport=false]/navigation-menu:duration-200',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { NavigationMenu as NavigationMenuPrimitive } from 'bits-ui';
|
||||
import { cn } from '../../tools';
|
||||
|
||||
let { ref = $bindable(null), class: className, ...restProps }: NavigationMenuPrimitive.IndicatorProps = $props();
|
||||
</script>
|
||||
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
bind:ref
|
||||
data-slot="navigation-menu-indicator"
|
||||
class={cn(
|
||||
'ui:data-[state=visible]:animate-in ui:data-[state=hidden]:animate-out ui:data-[state=hidden]:fade-out ui:data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
<div class="ui:bg-border ui:rounded-ts-sm ui:relative ui:top-[60%] ui:h-2 ui:w-2 ui:rotate-45 ui:shadow-md"></div>
|
||||
</NavigationMenuPrimitive.Indicator>
|
||||
@@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { NavigationMenu as NavigationMenuPrimitive } from 'bits-ui';
|
||||
import { cn } from '../../tools';
|
||||
|
||||
let { ref = $bindable(null), class: className, ...restProps }: NavigationMenuPrimitive.ItemProps = $props();
|
||||
</script>
|
||||
|
||||
<NavigationMenuPrimitive.Item
|
||||
bind:ref
|
||||
data-slot="navigation-menu-item"
|
||||
class={cn('ui:relative', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user