ajout page calendrier
This commit is contained in:
parent
8aa4e107ac
commit
20a8445a5f
14 changed files with 617 additions and 5 deletions
|
@ -30,6 +30,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
|
"node_modules/angular-calendar/css/angular-calendar.css",
|
||||||
"src/styles.scss"
|
"src/styles.scss"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -88,6 +89,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
|
"node_modules/angular-calendar/css/angular-calendar.css",
|
||||||
"src/styles.scss"
|
"src/styles.scss"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
118
frontend/package-lock.json
generated
118
frontend/package-lock.json
generated
|
@ -14,6 +14,11 @@
|
||||||
"@angular/forms": "^20.3.0",
|
"@angular/forms": "^20.3.0",
|
||||||
"@angular/platform-browser": "^20.3.0",
|
"@angular/platform-browser": "^20.3.0",
|
||||||
"@angular/router": "^20.3.0",
|
"@angular/router": "^20.3.0",
|
||||||
|
"angular-calendar": "^0.32.0",
|
||||||
|
"angular-draggable-droppable": "^9.0.1",
|
||||||
|
"angular-resizable-element": "^8.0.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
|
"moment": "^2.0.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.15.0"
|
"zone.js": "~0.15.0"
|
||||||
|
@ -1952,6 +1957,12 @@
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@mattlewis92/dom-autoscroller": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mattlewis92/dom-autoscroller/-/dom-autoscroller-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-YbrUWREPGEjE/FU6foXcAT1YbVwqD/jkYnY1dFb0o4AxtP3s4xKBthlELjndZih8uwsDWgQZx1eNskRNe2BgZQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@modelcontextprotocol/sdk": {
|
"node_modules/@modelcontextprotocol/sdk": {
|
||||||
"version": "1.17.3",
|
"version": "1.17.3",
|
||||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz",
|
||||||
|
@ -3354,6 +3365,13 @@
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@scarf/scarf": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/@schematics/angular": {
|
"node_modules/@schematics/angular": {
|
||||||
"version": "20.3.4",
|
"version": "20.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.4.tgz",
|
||||||
|
@ -3657,6 +3675,60 @@
|
||||||
"node": ">= 14.0.0"
|
"node": ">= 14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/angular-calendar": {
|
||||||
|
"version": "0.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-calendar/-/angular-calendar-0.32.0.tgz",
|
||||||
|
"integrity": "sha512-+iQ4j04SCxFv75bp8psx0Q9S7eefREE2IbwbeaSA3uqJcl7Rm1uo7mBwF9Pcg9gu6+U1NDVYlWzDsBpL9b4u3w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@scarf/scarf": "^1.1.1",
|
||||||
|
"calendar-utils": "^0.12.3",
|
||||||
|
"positioning": "^3.0.0",
|
||||||
|
"tslib": "^2.4.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/mattlewis92"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/core": ">=20.2.0",
|
||||||
|
"angular-draggable-droppable": "^9.0.1",
|
||||||
|
"angular-resizable-element": "^8.0.0",
|
||||||
|
"date-fns": "^4.0.0",
|
||||||
|
"moment": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"date-fns": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"moment": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/angular-draggable-droppable": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-draggable-droppable/-/angular-draggable-droppable-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-nxxFzBMEzB6RsRUqnHWelt9G7QXG2wc18czYL75YhJ9IkBJOBkxXBd0ZOTeDgV9C3mmkkw+PMLJOayx0GH6gXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@mattlewis92/dom-autoscroller": "^2.4.2",
|
||||||
|
"tslib": "^2.4.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/core": ">=20.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/angular-resizable-element": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-resizable-element/-/angular-resizable-element-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-cHCfz4y/G8GiKS4WHDeRPv5NPZA4BnsnEgn+z/l7wGhAQv28g7fFRRVyWQu4ZFM5YGu/d7Irv1kUGZ02pCHVdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/core": ">=20.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-escapes": {
|
"node_modules/ansi-escapes": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.1.tgz",
|
||||||
|
@ -4017,6 +4089,28 @@
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/calendar-utils": {
|
||||||
|
"version": "0.12.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/calendar-utils/-/calendar-utils-0.12.4.tgz",
|
||||||
|
"integrity": "sha512-OYhqJdeDRRVUdMYJMUrXtR7Go+80oEcA6VK4T3zjcUloQqcOVQKs/kp7j8Yw0HiLOcGwzF50WhLmzZoUNfkM7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"date-fns": "^4.0.0",
|
||||||
|
"luxon": "^3.0.0",
|
||||||
|
"moment": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"date-fns": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"luxon": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"moment": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/call-bind-apply-helpers": {
|
"node_modules/call-bind-apply-helpers": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
|
@ -4439,6 +4533,15 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/date-fns": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/kossnocorp"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/date-format": {
|
"node_modules/date-format": {
|
||||||
"version": "4.0.14",
|
"version": "4.0.14",
|
||||||
"resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
|
"resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
|
||||||
|
@ -7009,6 +7112,15 @@
|
||||||
"mkdirp": "bin/cmd.js"
|
"mkdirp": "bin/cmd.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.30.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mrmime": {
|
"node_modules/mrmime": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||||
|
@ -7795,6 +7907,12 @@
|
||||||
"node": ">=16.20.0"
|
"node": ">=16.20.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/positioning": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/positioning/-/positioning-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-cqg00fwFtEu14YwlLUuvFih5ztTd9RYUguJA55lWjeIGFQTpik7ca+TMU87YhAgwWjyjcGIG6l80eUh7X6uHog==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.6",
|
"version": "8.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||||
|
|
|
@ -28,6 +28,11 @@
|
||||||
"@angular/forms": "^20.3.0",
|
"@angular/forms": "^20.3.0",
|
||||||
"@angular/platform-browser": "^20.3.0",
|
"@angular/platform-browser": "^20.3.0",
|
||||||
"@angular/router": "^20.3.0",
|
"@angular/router": "^20.3.0",
|
||||||
|
"angular-calendar": "^0.32.0",
|
||||||
|
"angular-draggable-droppable": "^9.0.1",
|
||||||
|
"angular-resizable-element": "^8.0.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
|
"moment": "^2.0.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.15.0"
|
"zone.js": "~0.15.0"
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import {Home} from './pages/home/home';
|
import {Home} from './pages/home/home';
|
||||||
|
import { Agenda } from './pages/agenda/agenda';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{
|
{
|
||||||
path : '',
|
path : '',
|
||||||
component: Home
|
component: Home
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path : 'agenda',
|
||||||
|
component: Agenda
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
import { Component, signal } from '@angular/core';
|
import { Component, signal } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
import { CalendarPreviousViewDirective, CalendarTodayDirective, CalendarNextViewDirective, CalendarMonthViewComponent, CalendarWeekViewComponent, CalendarDayViewComponent, CalendarDatePipe, DateAdapter, provideCalendar } from 'angular-calendar';
|
||||||
|
import { adapterFactory } from 'angular-calendar/date-adapters/moment';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
export function momentAdapterFactory() {
|
||||||
|
return adapterFactory(moment);
|
||||||
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [RouterOutlet],
|
imports: [RouterOutlet, CalendarPreviousViewDirective, CalendarTodayDirective, CalendarNextViewDirective, CalendarMonthViewComponent, CalendarWeekViewComponent, CalendarDayViewComponent, CalendarDatePipe],
|
||||||
templateUrl: './app.html',
|
templateUrl: './app.html',
|
||||||
styleUrl: './app.scss'
|
styleUrl: './app.scss',
|
||||||
|
providers: [
|
||||||
|
provideCalendar({
|
||||||
|
provide: DateAdapter,
|
||||||
|
useFactory: momentAdapterFactory,
|
||||||
|
}),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class App {
|
export class App {
|
||||||
protected readonly title = signal('frontend');
|
protected readonly title = signal('frontend');
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { Component, EventEmitter, Input, Output, OnChanges, SimpleChanges, computed, effect, signal } from '@angular/core';
|
import { Component, EventEmitter, Input, Output, OnChanges, SimpleChanges, computed, effect, signal } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { NgFor, NgIf } from '@angular/common';
|
|
||||||
import oedb from '../../../oedb-types';
|
import oedb from '../../../oedb-types';
|
||||||
import { OedbApi } from '../../services/oedb-api';
|
import { OedbApi } from '../../services/oedb-api';
|
||||||
import { JsonPipe } from '@angular/common';
|
import { JsonPipe } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-edit-form',
|
selector: 'app-edit-form',
|
||||||
|
standalone: true,
|
||||||
imports: [ReactiveFormsModule, JsonPipe],
|
imports: [ReactiveFormsModule, JsonPipe],
|
||||||
templateUrl: './edit-form.html',
|
templateUrl: './edit-form.html',
|
||||||
styleUrl: './edit-form.scss'
|
styleUrl: './edit-form.scss'
|
||||||
|
|
|
@ -3,6 +3,7 @@ import oedb_what_categories from '../../../oedb-types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-all-events',
|
selector: 'app-all-events',
|
||||||
|
standalone: true,
|
||||||
imports: [],
|
imports: [],
|
||||||
templateUrl: './all-events.html',
|
templateUrl: './all-events.html',
|
||||||
styleUrl: './all-events.scss'
|
styleUrl: './all-events.scss'
|
||||||
|
|
59
frontend/src/app/pages/agenda/agenda.html
Normal file
59
frontend/src/app/pages/agenda/agenda.html
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<div class="agenda-container">
|
||||||
|
<div class="agenda-header">
|
||||||
|
<h1>Agenda des événements</h1>
|
||||||
|
<p>Événements des 20 derniers jours (10 jours avant et 10 jours après aujourd'hui)</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="agenda-content">
|
||||||
|
<div class="days-grid">
|
||||||
|
@for (day of daysWithEvents; track day.date.getTime()) {
|
||||||
|
<div class="day-card" [class.today]="isToday(day.date)" [class.past]="isPast(day.date)">
|
||||||
|
<div class="day-header">
|
||||||
|
<h3>{{ formatDate(day.date) }}</h3>
|
||||||
|
<span class="event-count">{{ day.events.length }} événement(s)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="events-list">
|
||||||
|
@if (day.events.length === 0) {
|
||||||
|
<p class="no-events">Aucun événement</p>
|
||||||
|
} @else {
|
||||||
|
@for (event of day.events; track event.id) {
|
||||||
|
<div class="event-item" (click)="selectEvent(event)">
|
||||||
|
<div class="event-time">{{ getEventTime(event) }}</div>
|
||||||
|
<div class="event-title">{{ getEventTitle(event) }}</div>
|
||||||
|
@if (event.properties.what) {
|
||||||
|
<div class="event-type">{{ event.properties.what }}</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Panneau latéral pour les détails de l'événement -->
|
||||||
|
@if (showSidePanel && selectedEvent) {
|
||||||
|
<div class="side-panel">
|
||||||
|
<div class="side-panel-header">
|
||||||
|
<h2>Détails de l'événement</h2>
|
||||||
|
<button class="close-btn" (click)="closeSidePanel()">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="side-panel-content">
|
||||||
|
<app-edit-form
|
||||||
|
[selected]="selectedEvent"
|
||||||
|
(saved)="onEventSaved($event)"
|
||||||
|
(created)="onEventCreated($event)"
|
||||||
|
(deleted)="onEventDeleted($event)">
|
||||||
|
</app-edit-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Overlay pour fermer le panneau latéral -->
|
||||||
|
@if (showSidePanel) {
|
||||||
|
<div class="overlay" (click)="closeSidePanel()"></div>
|
||||||
|
}
|
||||||
|
</div>
|
216
frontend/src/app/pages/agenda/agenda.scss
Normal file
216
frontend/src/app/pages/agenda/agenda.scss
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
.agenda-container {
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agenda-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.agenda-content {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.days-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-card {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.today {
|
||||||
|
border-color: #007bff;
|
||||||
|
background: #f8f9ff;
|
||||||
|
|
||||||
|
.day-header h3 {
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.past {
|
||||||
|
opacity: 0.7;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-count {
|
||||||
|
background: #007bff;
|
||||||
|
color: white;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.events-list {
|
||||||
|
min-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-events {
|
||||||
|
color: #999;
|
||||||
|
font-style: italic;
|
||||||
|
text-align: center;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-item {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e9ecef;
|
||||||
|
border-color: #007bff;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-type {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #007bff;
|
||||||
|
background: #e7f3ff;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panneau latéral
|
||||||
|
.side-panel {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 400px;
|
||||||
|
height: 100vh;
|
||||||
|
background: white;
|
||||||
|
box-shadow: -2px 0 10px rgba(0,0,0,0.1);
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-panel-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
background: #f8f9fa;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #666;
|
||||||
|
padding: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e9ecef;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-panel-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.agenda-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.days-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-panel {
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
}
|
23
frontend/src/app/pages/agenda/agenda.spec.ts
Normal file
23
frontend/src/app/pages/agenda/agenda.spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { Agenda } from './agenda';
|
||||||
|
|
||||||
|
describe('Agenda', () => {
|
||||||
|
let component: Agenda;
|
||||||
|
let fixture: ComponentFixture<Agenda>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [Agenda]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(Agenda);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
166
frontend/src/app/pages/agenda/agenda.ts
Normal file
166
frontend/src/app/pages/agenda/agenda.ts
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { OedbApi } from '../../services/oedb-api';
|
||||||
|
import { EditForm } from '../../forms/edit-form/edit-form';
|
||||||
|
|
||||||
|
interface Event {
|
||||||
|
id: string;
|
||||||
|
properties: {
|
||||||
|
label?: string;
|
||||||
|
name?: string;
|
||||||
|
what?: string;
|
||||||
|
start?: string;
|
||||||
|
when?: string;
|
||||||
|
stop?: string;
|
||||||
|
description?: string;
|
||||||
|
where?: string;
|
||||||
|
};
|
||||||
|
geometry?: {
|
||||||
|
type: string;
|
||||||
|
coordinates: [number, number];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DayEvents {
|
||||||
|
date: Date;
|
||||||
|
events: Event[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-agenda',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, EditForm],
|
||||||
|
templateUrl: './agenda.html',
|
||||||
|
styleUrl: './agenda.scss'
|
||||||
|
})
|
||||||
|
export class Agenda implements OnInit {
|
||||||
|
private oedbApi = inject(OedbApi);
|
||||||
|
|
||||||
|
events: Event[] = [];
|
||||||
|
daysWithEvents: DayEvents[] = [];
|
||||||
|
selectedEvent: Event | null = null;
|
||||||
|
showSidePanel = false;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.loadEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadEvents() {
|
||||||
|
const today = new Date();
|
||||||
|
const startDate = new Date(today);
|
||||||
|
startDate.setDate(today.getDate() - 10);
|
||||||
|
|
||||||
|
const endDate = new Date(today);
|
||||||
|
endDate.setDate(today.getDate() + 10);
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
start: `${startDate.toISOString().split('T')[0]}`,
|
||||||
|
end: `${endDate.toISOString().split('T')[0]}`,
|
||||||
|
limit: 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
this.oedbApi.getEvents(params).subscribe((response: any) => {
|
||||||
|
this.events = Array.isArray(response?.features) ? response.features : [];
|
||||||
|
this.organizeEventsByDay();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
organizeEventsByDay() {
|
||||||
|
const daysMap = new Map<string, DayEvents>();
|
||||||
|
|
||||||
|
// Initialiser les 20 jours
|
||||||
|
for (let i = -10; i <= 10; i++) {
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() + i);
|
||||||
|
const dateKey = date.toISOString().split('T')[0];
|
||||||
|
daysMap.set(dateKey, {
|
||||||
|
date: new Date(date),
|
||||||
|
events: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Organiser les événements par jour
|
||||||
|
this.events.forEach(event => {
|
||||||
|
const eventDate = this.getEventDate(event);
|
||||||
|
if (eventDate) {
|
||||||
|
const dateKey = eventDate.toISOString().split('T')[0];
|
||||||
|
const dayEvents = daysMap.get(dateKey);
|
||||||
|
if (dayEvents) {
|
||||||
|
dayEvents.events.push(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.daysWithEvents = Array.from(daysMap.values()).sort((a, b) =>
|
||||||
|
a.date.getTime() - b.date.getTime()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEventDate(event: Event): Date | null {
|
||||||
|
const startDate = event.properties.start || event.properties.when;
|
||||||
|
if (startDate) {
|
||||||
|
return new Date(startDate);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEventTitle(event: Event): string {
|
||||||
|
return event.properties.label || event.properties.name || 'Événement sans titre';
|
||||||
|
}
|
||||||
|
|
||||||
|
getEventTime(event: Event): string {
|
||||||
|
const startDate = event.properties.start || event.properties.when;
|
||||||
|
if (startDate) {
|
||||||
|
const date = new Date(startDate);
|
||||||
|
return date.toLocaleTimeString('fr-FR', {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
selectEvent(event: Event) {
|
||||||
|
this.selectedEvent = event;
|
||||||
|
this.showSidePanel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSidePanel() {
|
||||||
|
this.showSidePanel = false;
|
||||||
|
this.selectedEvent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onEventSaved(event: any) {
|
||||||
|
this.loadEvents(); // Recharger les événements après modification
|
||||||
|
this.closeSidePanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
onEventCreated(event: any) {
|
||||||
|
this.loadEvents(); // Recharger les événements après création
|
||||||
|
this.closeSidePanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
onEventDeleted(event: any) {
|
||||||
|
this.loadEvents(); // Recharger les événements après suppression
|
||||||
|
this.closeSidePanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
isToday(date: Date): boolean {
|
||||||
|
const today = new Date();
|
||||||
|
return date.toDateString() === today.toDateString();
|
||||||
|
}
|
||||||
|
|
||||||
|
isPast(date: Date): boolean {
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
return date < today;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatDate(date: Date): string {
|
||||||
|
return date.toLocaleDateString('fr-FR', {
|
||||||
|
weekday: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import { OedbApi } from '../../services/oedb-api';
|
||||||
import { UnlocatedEvents } from '../../shared/unlocated-events/unlocated-events';
|
import { UnlocatedEvents } from '../../shared/unlocated-events/unlocated-events';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
Menu,
|
Menu,
|
||||||
AllEvents,
|
AllEvents,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<menu>
|
<menu>
|
||||||
OpenEventDatabase
|
OpenEventDatabase
|
||||||
|
<a routerLink="/agenda">agenda</a>
|
||||||
<a href="/demo/stats">stats</a>
|
<a href="/demo/stats">stats</a>
|
||||||
<a href="https://source.cipherbliss.com/tykayn/oedb-backend">sources</a>
|
<a href="https://source.cipherbliss.com/tykayn/oedb-backend">sources</a>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import oedb_what_categories from '../../../../oedb-types';
|
import oedb_what_categories from '../../../../oedb-types';
|
||||||
|
import { RouterLink } from "@angular/router";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-menu',
|
selector: 'app-menu',
|
||||||
imports: [],
|
standalone: true,
|
||||||
|
imports: [RouterLink],
|
||||||
templateUrl: './menu.html',
|
templateUrl: './menu.html',
|
||||||
styleUrl: './menu.scss'
|
styleUrl: './menu.scss'
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue