diff --git a/flightscore/src/app-root.ts b/flightscore/src/app-root.ts index 7dc042e..741b849 100644 --- a/flightscore/src/app-root.ts +++ b/flightscore/src/app-root.ts @@ -1,18 +1,22 @@ import { LitElement, html, css } from 'lit'; -import { customElement } from 'lit/decorators.js'; +import { customElement, state } from 'lit/decorators.js'; import './pages/not-found-page'; import './components/nav-bar'; +import './components/cc/cc-nav-bar'; import './components/footer-bar'; import { Router } from './router/router'; import './pages/home-page'; import './pages/competition-page'; import './pages/auth/login-page'; import './pages/auth/register-page'; +import './pages/cc/cc-home-page'; @customElement('app-root') export class AppRoot extends LitElement { private router!: Router; + @state() private isCompetitionCenter = false; + static styles = css` :host { display: flex; @@ -41,14 +45,22 @@ export class AppRoot extends LitElement { { path: '/competitions', view: () => document.createElement('competition-page') }, { path: '/login', view: () => document.createElement('login-page') }, { path: '/register', view: () => document.createElement('register-page') }, + { path: '/cc/', view: () => document.createElement('cc-home-page') }, ]); + + this.router.onRouteChange = (path: string) => { + this.isCompetitionCenter = path.startsWith('/cc'); + }; + this.router.resolve(); this.addEventListener('nav', (e: any) => this.router.navigate(e.detail.path)); } render() { return html` - + ${this.isCompetitionCenter + ? html`` + : html``}
`; diff --git a/flightscore/src/components/cc/cc-nav-bar.css b/flightscore/src/components/cc/cc-nav-bar.css new file mode 100644 index 0000000..bc9fb06 --- /dev/null +++ b/flightscore/src/components/cc/cc-nav-bar.css @@ -0,0 +1,76 @@ +nav { + backdrop-filter: blur(10px); + background: var(--color-bg-nav); + border-bottom: 1px solid var(--color-border); + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem 1.5rem; + position: sticky; + top: 0; + z-index: 100; +} + +.brand { + font-weight: 700; + font-size: 1.1rem; + user-select: none; + width: fit-content; + display: flex; + justify-content: center; + align-items: center; + padding-right: 1rem; + +} + +.brand img { + width: auto; + height: 2rem; + padding-right: 1rem; +} + +.links { + display: flex; + gap: 1.25rem; + align-items: center; +} + +.links a { + text-decoration: none; +} + +a { + color: var(--color-text); + font-weight: 500; + position: relative; +} + +a::after { + content: ''; + position: absolute; + bottom: -6px; + left: 0; + width: 0%; + height: 2px; + background: var(--color-accent); + transition: 0.3s ease; +} + +a:hover::after { + width: 100%; +} + +button { + background: none; + border: 1px solid var(--color-border); + border-radius: 5px; + padding: 0.4rem 0.6rem; + color: var(--color-text); + cursor: pointer; + transition: all 0.3s ease; +} + +button:hover { + border-color: var(--color-accent); + color: var(--color-accent); +} \ No newline at end of file diff --git a/flightscore/src/components/cc/cc-nav-bar.ts b/flightscore/src/components/cc/cc-nav-bar.ts new file mode 100644 index 0000000..6b4a0ff --- /dev/null +++ b/flightscore/src/components/cc/cc-nav-bar.ts @@ -0,0 +1,49 @@ +import { LitElement, html, css, unsafeCSS } from 'lit'; +import { customElement, state } from 'lit/decorators.js'; +import styles from './cc-nav-bar.css?inline'; +import '../ui-link'; + +@customElement('cc-nav-bar') +export class CCNavBar extends LitElement { + static styles = css`${unsafeCSS(styles)}`; + + @state() theme: 'light' | 'dark' = + (localStorage.getItem('theme') as 'light' | 'dark') || + (window.matchMedia('(prefers-color-scheme: dark)').matches + ? 'dark' + : 'light'); + + firstUpdated() { + document.documentElement.setAttribute('data-theme', this.theme); + } + + toggleTheme() { + this.theme = this.theme === 'light' ? 'dark' : 'light'; + document.documentElement.setAttribute('data-theme', this.theme); + localStorage.setItem('theme', this.theme); + } + + navigate(path: string) { + this.dispatchEvent( + new CustomEvent('nav', { detail: { path }, bubbles: true, composed: true }) + ); + } + + render() { + return html` + + `; + } +} \ No newline at end of file diff --git a/flightscore/src/components/nav-bar.ts b/flightscore/src/components/nav-bar.ts index f2426d8..ab4332b 100644 --- a/flightscore/src/components/nav-bar.ts +++ b/flightscore/src/components/nav-bar.ts @@ -38,6 +38,7 @@ export class NavBar extends LitElement { Competitions Login + diff --git a/flightscore/src/pages/auth/login-page.ts b/flightscore/src/pages/auth/login-page.ts index 643d172..9e9ef01 100644 --- a/flightscore/src/pages/auth/login-page.ts +++ b/flightscore/src/pages/auth/login-page.ts @@ -68,7 +68,12 @@ export class LoginPage extends LitElement { password: this.password, }); localStorage.setItem('token', res.token); - window.dispatchEvent(new CustomEvent('auth-changed')); // for later reactive flows + window.dispatchEvent(new CustomEvent('auth-changed')); + dispatchEvent(new CustomEvent('nav', { + detail: { path: "/cc/" }, + bubbles: true, + composed: true, + })); } catch (e: any) { this.error = e.message || 'Login failed'; } finally { diff --git a/flightscore/src/pages/cc/cc-home-page.ts b/flightscore/src/pages/cc/cc-home-page.ts new file mode 100644 index 0000000..e4322ad --- /dev/null +++ b/flightscore/src/pages/cc/cc-home-page.ts @@ -0,0 +1,12 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; + +@customElement('cc-home-page') +export class CCHomePage extends LitElement { + render() { + return html` +

Welcome to the Competition Center Homepage

+

Analyze your tracks visually.

+ `; + } +} \ No newline at end of file diff --git a/flightscore/src/pages/competition-page.ts b/flightscore/src/pages/competition-page.ts index fd84c2d..e48e76a 100644 --- a/flightscore/src/pages/competition-page.ts +++ b/flightscore/src/pages/competition-page.ts @@ -6,6 +6,13 @@ export class CompetitionPage extends LitElement { render() { return html`

Welcome to the Competitions

+

Welcome to the Competitions

+

Welcome to the Competitions

+

Welcome to the Competitions

+

Welcome to the Competitions

+

Welcome to the Competitions

+

Welcome to the Competitions

+

Welcome to the Competitions

`; } } \ No newline at end of file diff --git a/flightscore/src/router/router.ts b/flightscore/src/router/router.ts index 01a6f73..2df6b9a 100644 --- a/flightscore/src/router/router.ts +++ b/flightscore/src/router/router.ts @@ -7,6 +7,7 @@ export type Route = { export class Router { private routes: Route[]; private outlet: HTMLElement; + onRouteChange?: (path: string) => void; constructor(outlet: HTMLElement, routes: Route[]) { this.routes = routes; @@ -26,5 +27,9 @@ export class Router { this.outlet.append( match ? match.view() : document.createElement('not-found-page') ); + + if (this.onRouteChange) { + this.onRouteChange(path); + } } } \ No newline at end of file