Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.1.4] - 2026-02-05

### Changed

- Rethought Data Security

### Documentation

- Version bumped to 1.1.4 in `package.json` and sidebar (AppShell).
- Root layout: documented PrivacyNotice, Toaster (sonner), metadata (title template, OG, Twitter), and ld+json WebApplication schema in layout and routes docs.
- Route pages: documented inline `metadata` export and `skeleton` prop pattern for feature components (StudentForm, QuizEditor, QuizPlayCard, GeneratorCard).
- Components: added PrivacyNotice and Toaster to layout section; Toaster (sonner) to UI primitives; sonner to dependencies.
- Structure: added `privacy-notice.tsx`, `robots.ts`, `sitemap.ts`, and Sonner/Toaster in folder structure and key files.
- Routes: updated App Layout and Route Metadata to reflect current layout content and route-level metadata; Route Components updated for skeleton prop usage.

## [1.1.3] - 2026-02-05

### Added
Expand Down
30 changes: 3 additions & 27 deletions app/breakout-rooms/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,10 @@ import { Metadata } from 'next';

import BreakoutGroupsCard from '@/components/breakout/breakout-groups-card';

/**
* Builds SEO metadata for the breakout room generator route.
*/
export const metadata: Metadata = {
title: 'Breakout Rooms | TeacherBuddy',
description: 'Create randomized student groups for breakout sessions.',
openGraph: {
title: 'Breakout Rooms | TeacherBuddy',
description: 'Create randomized student groups for breakout sessions.',
siteName: 'TeacherBuddy',
images: [
{
url: 'https://teacherbuddy.mrbubbles-src.dev/api/og',
width: 1200,
height: 630,
alt: 'TeacherBuddy Logo',
},
],
type: 'website',
locale: 'en_GB',
},
twitter: {
card: 'summary_large_image',
title: 'Breakout Rooms | TeacherBuddy',
description: 'Create randomized student groups for breakout sessions.',
images: ['https://teacherbuddy.mrbubbles-src.dev/api/og'],
creator: '@_MstrBubbles',
},
title: 'Breakout Rooms',
description:
'Instantly create randomised student groups for breakout sessions, discussions, or team activities.',
};

/**
Expand Down
30 changes: 3 additions & 27 deletions app/generator/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,10 @@ import { Metadata } from 'next';
import GeneratorCard from '@/components/generator/generator-card';
import GeneratorCardSkeleton from '@/components/loading/generator-card-skeleton';

/**
* Builds SEO metadata for the random generator route.
*/
export const metadata: Metadata = {
title: 'Student Generator | TeacherBuddy',
description: 'Pick a random student without repeats.',
openGraph: {
title: 'Student Generator | TeacherBuddy',
description: 'Pick a random student without repeats.',
siteName: 'TeacherBuddy',
images: [
{
url: 'https://teacherbuddy.mrbubbles-src.dev/api/og',
width: 1200,
height: 630,
alt: 'TeacherBuddy Logo',
},
],
type: 'website',
locale: 'en_GB',
},
twitter: {
card: 'summary_large_image',
title: 'Student Generator | TeacherBuddy',
description: 'Pick a random student without repeats.',
images: ['https://teacherbuddy.mrbubbles-src.dev/api/og'],
creator: '@_MstrBubbles',
},
title: 'Student Generator',
description:
'Randomly pick students from your roster without repeats — perfect for fair participation in class.',
};

/**
Expand Down
45 changes: 45 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -384,3 +384,48 @@
[data-description] {
color: inherit;
}

/* Timer volume slider — filled track with --primary color */
.timer-volume-slider {
--slider-pct: 0%;
accent-color: var(--primary);
}
.timer-volume-slider::-webkit-slider-runnable-track {
height: 0.5rem;
border-radius: 9999px;
background: linear-gradient(
to right,
var(--primary) var(--slider-pct),
var(--muted) var(--slider-pct)
);
}
.timer-volume-slider::-moz-range-track {
height: 0.5rem;
border-radius: 9999px;
background: linear-gradient(
to right,
var(--primary) var(--slider-pct),
var(--muted) var(--slider-pct)
);
}
.timer-volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 1rem;
height: 1rem;
border-radius: 9999px;
background: var(--primary);
border: 2px solid var(--background);
box-shadow: 0 1px 3px oklch(0 0 0 / 0.2);
margin-top: -0.25rem;
cursor: pointer;
}
.timer-volume-slider::-moz-range-thumb {
width: 1rem;
height: 1rem;
border-radius: 9999px;
background: var(--primary);
border: 2px solid var(--background);
box-shadow: 0 1px 3px oklch(0 0 0 / 0.2);
cursor: pointer;
}
89 changes: 39 additions & 50 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,41 +21,37 @@ const geistMono = Geist_Mono({
subsets: ['latin'],
});

export async function generateMetadata(): Promise<Metadata> {
return {
metadataBase: new URL(
process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000',
),
title: 'TeacherBuddy',
description:
'Manage students, run quizzes, and organize class activities in one place.',
openGraph: {
title: 'TeacherBuddy',
description:
'Manage students, run quizzes, and organize class activities in one place.',
siteName: 'TeacherBuddy',
images: [
{
url: 'https://teacherbuddy.mrbubbles-src.dev/api/og',
width: 1200,
height: 630,
alt: 'TeacherBuddy Logo',
},
],
type: 'website',
locale: 'en_GB',
},
twitter: {
card: 'summary_large_image',
title: 'TeacherBuddy',
description:
'Manage students, run quizzes, and organize class activities in one place.',
images: ['https://teacherbuddy.mrbubbles-src.dev/api/og'],
creator: '@_MstrBubbles',
},
other: { 'apple-mobile-web-app-title': 'teacherbuddy.mrbubbles-src.dev' },
};
}
export const metadata: Metadata = {
metadataBase: new URL(
process.env.NEXT_PUBLIC_SITE_URL ??
'https://teacherbuddy.mrbubbles-src.dev',
),
title: {
template: '%s | TeacherBuddy',
default: 'TeacherBuddy — Free Classroom Tools for Teachers',
},
description:
'Manage students, run quizzes, and organize class activities in one place.',
openGraph: {
siteName: 'TeacherBuddy',
images: [
{
url: '/api/og',
width: 1200,
height: 630,
alt: 'TeacherBuddy — free classroom management dashboard',
},
],
type: 'website',
locale: 'en_GB',
},
twitter: {
card: 'summary_large_image',
images: ['/api/og'],
creator: '@_MstrBubbles',
},
other: { 'apple-mobile-web-app-title': 'TeacherBuddy' },
};

/**
* Renders the shared HTML shell and providers for all application routes.
Expand All @@ -67,13 +63,8 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<html lang="en_GB" suppressHydrationWarning>
<head>
<meta
property="og:url"
content="https://teacherbuddy.mrbubbles-src.dev"
/>
<link rel="canonical" href="https://teacherbuddy.mrbubbles-src.dev" />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
Expand All @@ -84,16 +75,14 @@ export default function RootLayout({
description:
'Manage students, run quizzes, and organize class activities in one place.',
url: 'https://teacherbuddy.mrbubbles-src.dev',
applicationCategory: 'Education',
applicationCategory: 'EducationalApplication',
applicationSubCategory: 'Classroom Management',
applicationSuite: 'TeacherBuddy',
applicationVersion: '1.1.3',
applicationInstallUrl:
'https://teacherbuddy.mrbubbles-src.dev/install',
applicationUpdateUrl:
'https://teacherbuddy.mrbubbles-src.dev/update',
applicationUpdateVersion: '1.1.3',
applicationUpdateStatus: 'available',
operatingSystem: 'Web',
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'EUR',
},
}),
}}
/>
Expand Down
2 changes: 2 additions & 0 deletions app/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"name": "TeacherBuddy",
"short_name": "TB",
"description": "Manage students, run quizzes, and organise class activities in one place.",
"start_url": "/",
"icons": [
{
"src": "/web-app-manifest-192x192.png",
Expand Down
22 changes: 2 additions & 20 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,9 @@
import Image from 'next/image';

import DashboardCards from '@/components/dashboard/dashboard-cards';
import Logo from '@/public/images/teacherbuddy-logo.png';

/**
* Renders the dashboard landing page with branding and primary workflow cards.
* Renders the dashboard landing page combining command center hero with timeline flow.
* Use this route as the entry point for navigating all TeacherBuddy tools.
*/
export default function Page() {
return (
<>
<section className="justify-self-center mb-5 max-w-full md:max-w-2/3 lg:max-w-2/4 xl:max-w-4/6">
<Image
src={Logo}
alt="TeacherBuddy Logo"
width={895}
height={372}
priority
placeholder="blur"
blurDataURL={Logo.blurDataURL}
/>
</section>
<DashboardCards />
</>
);
return <DashboardCards />;
}
30 changes: 3 additions & 27 deletions app/play/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,10 @@ import { Metadata } from 'next';
import QuizPlayCardSkeleton from '@/components/loading/quiz-play-card-skeleton';
import QuizPlayCard from '@/components/play/quiz-play-card';

/**
* Builds SEO metadata for the live quiz play route.
*/
export const metadata: Metadata = {
title: 'Quiz Play | TeacherBuddy',
description: 'Draw a student and a question, then reveal the answer.',
openGraph: {
title: 'Quiz Play | TeacherBuddy',
description: 'Draw a student and a question, then reveal the answer.',
siteName: 'TeacherBuddy',
images: [
{
url: 'https://teacherbuddy.mrbubbles-src.dev/api/og',
width: 1200,
height: 630,
alt: 'TeacherBuddy Logo',
},
],
type: 'website',
locale: 'en_GB',
},
twitter: {
card: 'summary_large_image',
title: 'Quiz Play | TeacherBuddy',
description: 'Draw a student and a question, then reveal the answer.',
images: ['https://teacherbuddy.mrbubbles-src.dev/api/og'],
creator: '@_MstrBubbles',
},
title: 'Quiz Play',
description:
'Run live quizzes in class — randomly draw a student and question, then reveal the answer together.',
};
/**
* Renders the live quiz play route.
Expand Down
27 changes: 3 additions & 24 deletions app/projects/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,9 @@ import ProjectListBuilder from '@/components/projects/project-list-builder';
import ProjectListView from '@/components/projects/project-list-view';

export const metadata: Metadata = {
title: 'Project Lists | TeacherBuddy',
description: 'Build project lists and group students from your roster.',
openGraph: {
title: 'Project Lists | TeacherBuddy',
description: 'Build project lists and group students from your roster.',
siteName: 'TeacherBuddy',
images: [
{
url: 'https://teacherbuddy.mrbubbles-src.dev/api/og',
width: 1200,
height: 630,
alt: 'TeacherBuddy Logo',
},
],
type: 'website',
locale: 'en_GB',
},
twitter: {
card: 'summary_large_image',
title: 'Project Lists | TeacherBuddy',
description: 'Build project lists and group students from your roster.',
images: ['https://teacherbuddy.mrbubbles-src.dev/api/og'],
creator: '@_MstrBubbles',
},
title: 'Project Lists',
description:
'Build project lists, assign students to teams, and organise group work from your class roster.',
};
/**
* Renders the project list management route.
Expand Down
Loading
Loading