embed share

This commit is contained in:
Tykayn 2025-11-03 00:08:06 +01:00 committed by tykayn
parent 5d636b0027
commit f8abb4d11a
20 changed files with 1040 additions and 72 deletions

View file

@ -0,0 +1,51 @@
<div class="embed-view-container">
<div class="embed-header">
<h2>Événements OEDB</h2>
</div>
@if (isLoading) {
<div class="embed-loading">
<div class="spinner"></div>
<p>Chargement des événements...</p>
</div>
} @else if (error) {
<div class="embed-error">
<p>{{error}}</p>
</div>
} @else if (events.length === 0) {
<div class="embed-empty">
<p>Aucun événement trouvé</p>
</div>
} @else {
<div class="embed-events-list">
@for (event of events; track event.id) {
<div class="embed-event-item">
<div class="embed-event-header">
<h3 class="embed-event-title">
{{event.properties?.label || event.properties?.name || 'Événement sans nom'}}
</h3>
@if (event.properties?.what) {
<span class="embed-event-type">{{event.properties.what}}</span>
}
</div>
<div class="embed-event-details">
<div class="embed-event-date">
📅 {{formatDate(event.properties?.start || event.properties?.when)}}
</div>
@if (event.properties?.where) {
<div class="embed-event-location">
📍 {{event.properties.where}}
</div>
}
@if (event.properties?.description) {
<div class="embed-event-description">
{{event.properties.description}}
</div>
}
</div>
</div>
}
</div>
}
</div>

View file

@ -0,0 +1,136 @@
.embed-view-container {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #ffffff;
color: #2c3e50;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.embed-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 16px 20px;
text-align: center;
h2 {
margin: 0;
font-size: 1.2rem;
font-weight: 600;
}
}
.embed-loading,
.embed-error,
.embed-empty {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
text-align: center;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #e9ecef;
border-top: 4px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 16px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.embed-error {
color: #e74c3c;
background: #fdf2f2;
}
.embed-empty {
color: #6c757d;
}
.embed-events-list {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 16px;
}
.embed-event-item {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 16px;
transition: all 0.2s ease;
border-left: 4px solid #667eea;
}
.embed-event-item:hover {
background: #ffffff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transform: translateX(2px);
}
.embed-event-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
gap: 12px;
}
.embed-event-title {
margin: 0;
font-size: 1.1rem;
font-weight: 600;
color: #2c3e50;
flex: 1;
}
.embed-event-type {
background: #667eea;
color: white;
padding: 4px 10px;
border-radius: 12px;
font-size: 0.75rem;
white-space: nowrap;
}
.embed-event-details {
display: flex;
flex-direction: column;
gap: 8px;
font-size: 0.9rem;
}
.embed-event-date,
.embed-event-location {
color: #6c757d;
}
.embed-event-description {
color: #495057;
margin-top: 8px;
line-height: 1.5;
}
/* Responsive */
@media (max-width: 768px) {
.embed-event-header {
flex-direction: column;
}
.embed-event-type {
align-self: flex-start;
}
}

View file

@ -0,0 +1,83 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { OedbApi } from '../../../services/oedb-api';
interface OedbEvent {
id: string;
properties: {
label?: string;
name?: string;
what?: string;
start?: string;
when?: string;
stop?: string;
description?: string;
where?: string;
};
}
@Component({
selector: 'app-embed-view',
standalone: true,
imports: [CommonModule],
templateUrl: './embed-view.html',
styleUrl: './embed-view.scss'
})
export class EmbedView implements OnInit {
private oedbApi = inject(OedbApi);
private route = inject(ActivatedRoute);
events: OedbEvent[] = [];
isLoading = true;
error: string | null = null;
ngOnInit() {
this.route.queryParams.subscribe(params => {
this.loadEvents(params);
});
}
loadEvents(params: any) {
this.isLoading = true;
this.error = null;
const apiParams: any = {
when: "NEXT365DAYS",
limit: params.limit ? Number(params.limit) : 50
};
if (params.what) apiParams.what = params.what;
if (params.start) apiParams.start = params.start;
if (params.end) apiParams.end = params.end;
this.oedbApi.getEvents(apiParams).subscribe({
next: (response: any) => {
this.events = Array.isArray(response?.features) ? response.features : [];
this.isLoading = false;
},
error: (err) => {
console.error('Error loading events:', err);
this.error = 'Erreur lors du chargement des événements';
this.isLoading = false;
}
});
}
formatDate(dateString: string | undefined): string {
if (!dateString) return '—';
try {
const date = new Date(dateString);
return date.toLocaleDateString('fr-FR', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch {
return dateString;
}
}
}

View file

@ -116,6 +116,17 @@
<option value="dark">Sombre</option>
</select>
</div>
<div class="form-group">
<label for="embedType">Type d'intégration :</label>
<select
id="embedType"
[(ngModel)]="config().embedType"
(ngModelChange)="updateConfig()">
<option value="iframe">Iframe (recommandé pour intégration simple)</option>
<option value="script">Script JavaScript (plus flexible)</option>
</select>
</div>
</div>
<div class="code-output">

View file

@ -12,6 +12,7 @@ interface EmbedConfig {
width: string;
height: string;
theme: string;
embedType: 'script' | 'iframe';
}
@Component({
@ -23,14 +24,15 @@ interface EmbedConfig {
})
export class Embed {
config = signal<EmbedConfig>({
apiUrl: 'https://api.openenventdatabase.org',
apiUrl: 'https://api.openeventdatabase.org',
what: 'culture',
start: '',
end: '',
limit: 50,
width: '100%',
height: '400px',
theme: 'light'
theme: 'light',
embedType: 'iframe'
});
generatedCode = signal<string>('');
@ -49,29 +51,46 @@ export class Embed {
if (config.what) params.set('what', config.what);
if (config.start) params.set('start', config.start);
if (config.end) params.set('end', config.end);
if (config.limit) params.set('limit', config.limit.toString());
if (config.limit) params.set('limit', config.limit.toString());
const queryString = params.toString();
const scriptUrl = `${window.location.origin}/embed.js`;
return `<!-- OpenEventDatabase Embed start-->
if (config.embedType === 'iframe') {
// Générer du code iframe qui pointe vers une page embed spéciale
const embedUrl = `${window.location.origin}/embed/view?${queryString}`;
return `<!-- OpenEventDatabase Embed (iframe) -->
<iframe
src="${embedUrl}"
width="${config.width}"
height="${config.height}"
frameborder="0"
scrolling="auto"
style="border: 1px solid #ddd; border-radius: 8px;">
</iframe>
<!-- OpenEventDatabase Embed end -->`;
} else {
// Code script (ancienne méthode)
const scriptUrl = `${window.location.origin}/embed.js`;
const paramsObj = queryString ?
queryString.split('&').map(param => {
const [key, value] = param.split('=');
return ` '${key}': '${decodeURIComponent(value || '')}'`;
}).join(',\n') : '';
const paramsCode = paramsObj ? ` params: {\n${paramsObj}\n },\n` : '';
return `<!-- OpenEventDatabase Embed (script) -->
<div id="oedb-events" style="width: ${config.width}; height: ${config.height}; border: 1px solid #ddd; border-radius: 8px; overflow: hidden;"></div>
<script src="${scriptUrl}"></script>
<script>
OEDBEmbed.init({
container: '#oedb-events',
apiUrl: '${config.apiUrl}',
params: {
${queryString ? queryString.split('&').map(param => `'${param.split('=')[0]}': '${param.split('=')[1]}'`).join(',\n ') : ''}
},
theme: '${config.theme}'
${paramsCode} theme: '${config.theme}'
});
</script>
<!--OpenEventDatabase Embed end-->
`;
<!-- OpenEventDatabase Embed end -->`;
}
}
copyToClipboard() {
@ -93,19 +112,37 @@ export class Embed {
<html>
<head>
<title>Aperçu OEDB Embed</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body { margin: 0; padding: 20px; font-family: Arial, sans-serif; }
.preview-container { max-width: 100%; }
.preview-container { max-width: 100%; margin-top: 20px; }
h2 { color: #2c3e50; }
</style>
</head>
<body>
<h2>Aperçu de l'intégration</h2>
<p>Voici comment l'intégration apparaîtra sur votre site :</p>
<div class="preview-container">
${code}
</div>
${config.embedType === 'script' ? `
<script>
// Attendre que le DOM soit chargé pour les scripts
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
// Le script embed.js se chargera automatiquement
});
}
</script>
` : ''}
</body>
</html>
`);
// Si c'est un iframe, le charger après que le document soit écrit
if (config.embedType === 'iframe') {
previewWindow.document.close();
}
}
}
}