migrate
This commit is contained in:
@@ -1,50 +0,0 @@
|
|||||||
# NGO Website — ODOO4projects
|
|
||||||
|
|
||||||
**Project:** NGO marketing homepage and blog
|
|
||||||
**URL:** [ngo.odoo4projects.com](https://ngo.odoo4projects.com)
|
|
||||||
**Stack:** Static HTML · Tailwind CSS · Vanilla JS
|
|
||||||
|
|
||||||
---
|
|
||||||
## BLOG POSTS
|
|
||||||
Blog posts live in `blog.json` — do not create separate backup files.
|
|
||||||
To add a blog post customize this command
|
|
||||||
jq '.arrays[0] = [$NEW_JSON] + .arrays[0]' blog.json > tmp.json && mv tmp.json blog.json
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Repository Rules
|
|
||||||
- Do not touch this readme
|
|
||||||
- Keep the seperation of Site and the blogposts in blog.json
|
|
||||||
Example of JSON structure for blog.json
|
|
||||||
{
|
|
||||||
"area": "Transparency",
|
|
||||||
"title": "Every Peso on the Page: Ana's Ledger Story",
|
|
||||||
"teaser": "<p>Ana replaced five notebooks with a four-stop impact ledger and now answers donor questions in seconds.</p>",
|
|
||||||
"content": "<h3>Every peso needs a map</h3>\n<p>Ana used to juggle five notebooks and still felt blind when a donor asked where their coin landed.</p>\n<h3>Pressure points</h3>\n<p without late nights.</p>\n",
|
|
||||||
"date": "2026-03-22"
|
|
||||||
},
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Page Structure
|
|
||||||
|
|
||||||
| # | Block | Purpose |
|
|
||||||
|---|-------|---------|
|
|
||||||
| 1 | **Hero** | Headline, subheadline, free-trial CTA, key stats |
|
|
||||||
| 2 | **Hot NGO Topics** | Three focused topic cards (transparency, communications, fundraising) |
|
|
||||||
| 3 | **Blog** | Latest articles loaded dynamically from `blog.json` |
|
|
||||||
| 4 | **Sign Up — Free Trial** | Email signup form, 4-week free trial, no credit card |
|
|
||||||
| 5 | **Have a Meeting** | 30-minute consultation booking via Google Calendar |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
## Languages
|
|
||||||
|
|
||||||
| Code | Language |
|
|
||||||
|------|----------|
|
|
||||||
| `en` | English (default) |
|
|
||||||
| `es` | Español |
|
|
||||||
| `de` | Deutsch |
|
|
||||||
|
|
||||||
All UI strings are managed via the inline `translations` object in `index.html`.
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,16 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"area": "Automatisierung",
|
|
||||||
"date": "2026-03-25",
|
|
||||||
"title": "NGO-Verwaltung um 20–40 % reduzieren: 7 Workflows, die du diesen Monat automatisieren kannst",
|
|
||||||
"teaser": "<p>Maria verbrachte ihre Abende mit Papierkram statt mit den Familien, denen sie helfen wollte. Mit Odoo und n8n reduzierte ihr Team den Verwaltungsaufwand in unter vier Wochen um ein Drittel.</p>",
|
|
||||||
"content": "<h3>Die Admin-Falle, die jede NGO kennt</h3><p>Verstreute Spenderdaten, wiederholte Kopier-Einfüge-Schritte und langsame Bestätigungen untergraben das Vertrauen und kosten wertvolle Energie. Ein ERP bündelt Kontakte, Spenden, Rechnungen und Projektaufgaben an einem zentralen Ort — niemand muss dieselben Daten mehr in fünf verschiedene Tools eintippen.</p><h3>Odoo als Kern, n8n als Verbindung</h3><p>Nutze Odoo für Kontakte, Spenden, Projekte und Dokumente. Erstelle dann n8n-Workflows, um automatische Spendenquittungen zu versenden, Kontaktdatensätze bei Formulareinreichungen zu aktualisieren, Folgeaufgaben zu terminieren und Zusammenfassungsberichte per Slack oder E-Mail zu verteilen.</p><h3>7 Workflows für diesen Monat</h3><ul><li>Sofortige Dankesnachricht an Spender bei jeder Zuwendung</li><li>Fördermittel-Compliance-Erinnerungen im 30-Tage-Rhythmus</li><li>Onboarding-Checkliste für Freiwillige automatisch bei der Anmeldung</li><li>Monatlicher Impact-Bericht automatisch erstellt und versendet</li><li>Dubletten-Erkennung bei jedem Datenimport</li><li>Rechnungsgenehmigung nach Projekt und Budgetposition geroutet</li><li>Ausgabenerfassung aus dem Feld mit Foto-Belegerfassung</li></ul><h3>Was du erwarten kannst</h3><p>Typische NGO-Implementierungen zeigen innerhalb weniger Wochen 20–40 % weniger Verwaltungsstunden. Marias Team hörte mit den nächtlichen Admin-Sprints auf und begann jeden Morgen mit einem einzigen Dashboard — mehr Zeit für Lehrercoaching, Gemeindebesuche und die Planung wirkungsvoller Programme.</p>"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"area": "Spenderkommunikation",
|
|
||||||
"date": "2026-03-18",
|
|
||||||
"title": "Wie Maya das Spender-Chaos in klare Verbindungen verwandelte",
|
|
||||||
"teaser": "<p>Maya verwandelte verstreute Spendernachrichten in einen reibungslosen, vertrauenswürdigen Ablauf, der die Unterstützung wachsen lässt und das gesamte Team auf Kurs hält.</p>",
|
|
||||||
"content": "<h3>Von verstreuten E-Mails zur einheitlichen Ansicht</h3><p>Maya jonglierte früher mit Tabellen, E-Mail-Threads und Haftnotizen, um Spenderinteraktionen zu verfolgen. Nachrichten gingen verloren, Dankesworte kamen verspätet an und das Vertrauen bröckelte. Sie wusste, dass sich etwas ändern musste.</p><h3>Den Spender-Journey strukturieren</h3><p>Sie kartierte jeden Kontaktpunkt: Erstkontakt, Spende, Nachfassaktionen, Erneuerungen und Sonderkampagnen. Jede Interaktion erhielt eine Karte in Odoo mit Spendername, Datum, bevorzugtem Kanal und Teamnotizen — für alle auf einen Blick sichtbar.</p><h3>Automatisierte Berührungspunkte</h3><p>n8n übernimmt die Routine: Spender werden innerhalb von Minuten nach einer Zuwendung gedankt, Zusagen werden bestätigt und das Team wird vor fälligen Erneuerungen benachrichtigt. Nichts fällt durchs Raster, und der persönliche Ton bleibt erhalten — dank Vorlagen, die auf jedes Spendersegment zugeschnitten sind.</p><h3>Transparenz auf allen Ebenen</h3><p>Spender können Updates zu den Projekten sehen, die sie unterstützt haben. Status-Labels in Kampagnen zeigen zugewiesene Mittel, Impact-Geschichten und bevorstehende Veranstaltungen. Mitarbeiter hinterlassen für Kollegen sichtbare Notizen — doppelte Kontaktaufnahmen und unangenehme Überschneidungen gehören der Vergangenheit an.</p><h3>Visuelle Echtzeit-Dashboards</h3><p>Ein Live-Dashboard zeigt Spenderaktivitäten, bevorstehende Erneuerungen und die Kampagnengesundheit auf einen Blick. Farbcodes setzen Prioritäten: Grün für bestätigte Gaben, Gelb für ausstehende Zusagen, Rot für dringende Nachfassaktionen, die menschliche Aufmerksamkeit erfordern.</p><h3>Kommunikationsrhythmus</h3><p>Wöchentliche Spender-Check-ins halten das Team auf Kurs. Monatliche Newsletter heben Erfolge, Spender-Impact und kommende Möglichkeiten hervor. Sonderkampagnen lösen personalisierte Micro-E-Mails aus, die auf Spenderlevel und Verlauf jeder Person abgestimmt sind.</p><ul><li>Montag: neue Spender und ausstehende Nachfassaktionen prüfen.</li><li>Mittwoch: Kampagnenfortschritt bestätigen und Spendernotizen aktualisieren.</li><li>Freitag: Dankesmails und Impact-Updates versenden.</li></ul><h3>Kennzahlen & Wirkung</h3><p>Die Spenderbindung verbesserte sich um 25 %. Reaktionszeiten sanken von Tagen auf Stunden. Mitarbeiter berichten von weniger Stress, weil Kommunikation berechenbar, sichtbar und nicht mehr vom Gedächtnis einer einzelnen Person abhängig ist.</p><h3>Dein Rollout-Plan</h3><ul><li>Alle Spenderdaten in Odoo-Karten importieren und Verantwortliche zuweisen.</li><li>n8n-Alerts für Gaben, Nachfassaktionen und bevorstehende Erneuerungen einrichten.</li><li>Dashboards mit aktiven Spendern, Zusagen und Kampagnenstatus erstellen.</li><li>Dankesmails und vierteljährliche Impact-Updates automatisieren.</li></ul><p>Vier Schritte ersetzen wochenlange verstreute E-Mails und verpasste Möglichkeiten — und jeder Spender beginnt sich wirklich wahrgenommen zu fühlen.</p>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"area": "Automatización",
|
|
||||||
"date": "2026-03-25",
|
|
||||||
"title": "Reduce el trabajo administrativo de tu ONG un 20–40 %: 7 flujos que puedes automatizar este mes",
|
|
||||||
"teaser": "<p>María pasaba sus noches sepultada en papeleo en lugar de ayudar a las familias que había venido a servir. Con Odoo y n8n, su equipo redujo el tiempo administrativo en un tercio en menos de cuatro semanas.</p>",
|
|
||||||
"content": "<h3>La trampa administrativa que toda ONG conoce</h3><p>Datos dispersos de donantes, pasos repetitivos de copiar y pegar y acuses de recibo lentos erosionan la confianza y agotan la energía del equipo. Un ERP centraliza contactos, donaciones, facturas y tareas de proyecto en un único lugar, de modo que nadie tenga que reintroducir los mismos datos en cinco herramientas distintas.</p><h3>Odoo como núcleo, n8n como conector</h3><p>Usa Odoo para Contactos, Donaciones, Proyectos y Documentos. Después crea flujos en n8n para enviar recibos automáticos a donantes, actualizar registros cuando se envían formularios, programar tareas de seguimiento y distribuir informes de resumen por Slack o correo electrónico.</p><h3>7 flujos para automatizar este mes</h3><ul><li>Correo de agradecimiento instantáneo al donante en cada aportación</li><li>Recordatorios de cumplimiento de subvenciones cada 30 días</li><li>Lista de incorporación de voluntarios activada al registrarse</li><li>Informe de impacto mensual generado y enviado automáticamente</li><li>Detección de contactos duplicados en cada importación</li><li>Enrutamiento de aprobaciones de facturas por proyecto y línea presupuestaria</li><li>Envío de gastos desde campo con captura de recibo fotográfico</li></ul><h3>Resultados esperados</h3><p>Las implementaciones habituales en ONG muestran un 20–40 % menos de horas administrativas en pocas semanas. El equipo de María dejó de hacer maratones nocturnos de administración y empezó cada mañana con un solo panel de control, liberando tiempo para capacitar a docentes, visitar comunidades y planificar programas de impacto.</p>"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"area": "Comunicación con Donantes",
|
|
||||||
"date": "2026-03-18",
|
|
||||||
"title": "Cómo Maya convirtió el caos de donantes en conexiones claras",
|
|
||||||
"teaser": "<p>Maya transformó los mensajes dispersos de donantes en un flujo fluido y confiable que hace crecer el apoyo y mantiene a todo el equipo alineado.</p>",
|
|
||||||
"content": "<h3>De correos dispersos a una visión única</h3><p>Maya solía manejar hojas de cálculo, hilos de correo y notas adhesivas para rastrear las interacciones con donantes. Se perdían mensajes, los agradecimientos llegaban tarde y la confianza comenzaba a tambalearse. Sabía que algo tenía que cambiar.</p><h3>Organizando el recorrido del donante</h3><p>Mapeó cada punto de contacto: contacto inicial, donación, seguimientos, renovaciones y campañas especiales. Cada interacción tuvo su tarjeta en Odoo con el nombre del donante, la fecha, el canal preferido y las notas del equipo, visible para todos de un vistazo.</p><h3>Puntos de contacto automatizados</h3><p>n8n automatiza la rutina: agradece a los donantes a los pocos minutos de recibir una aportación, confirma los compromisos y avisa al equipo antes de que venzan las renovaciones. Nadie se queda sin respuesta y el tono personal se mantiene intacto gracias a plantillas diseñadas para cada segmento de donantes.</p><h3>Transparencia en todos los niveles</h3><p>Los donantes pueden ver actualizaciones de los proyectos que apoyaron. Las etiquetas de estado en las campañas muestran los fondos asignados, historias de impacto y próximos eventos. El personal deja notas visibles para sus colegas, eliminando contactos duplicados y superposiciones incómodas.</p><h3>Paneles visuales en tiempo real</h3><p>Un panel en vivo muestra la actividad de donantes, las próximas renovaciones y la salud de la campaña de un solo vistazo. Los códigos de color marcan la prioridad: verde para donaciones confirmadas, amarillo para compromisos pendientes, rojo para seguimientos urgentes que requieren atención humana.</p><h3>Ritmo de comunicación</h3><p>Las revisiones semanales de donantes mantienen al equipo alineado. Los boletines mensuales destacan logros, impacto de los donantes y próximas oportunidades. Las campañas especiales activan microemails personalizados según el nivel de donación y el historial de cada persona.</p><ul><li>Lunes: revisar nuevos donantes y seguimientos pendientes.</li><li>Miércoles: confirmar el avance de las campañas y actualizar las notas de donantes.</li><li>Viernes: enviar correos de agradecimiento y actualizaciones de impacto.</li></ul><h3>Métricas e impacto</h3><p>La retención de donantes mejoró un 25 %. Los tiempos de respuesta bajaron de días a horas. El personal reporta menos estrés porque la comunicación es predecible, visible y ya no depende de la memoria de una sola persona.</p><h3>Tu plan de implementación</h3><ul><li>Importar todos los registros de donantes a Odoo y asignar responsables.</li><li>Configurar alertas en n8n para donaciones, seguimientos y renovaciones.</li><li>Crear paneles con donantes activos, compromisos y estado de campaña.</li><li>Automatizar correos de agradecimiento y actualizaciones de impacto trimestrales.</li></ul><p>Cuatro pasos reemplazan semanas de correos dispersos y oportunidades perdidas — y cada donante empieza a sentirse genuinamente valorado.</p>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
-685
@@ -1,685 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>Dashboard</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
width: 1920px;
|
|
||||||
height: 1200px;
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
background: #2f3e4e;
|
|
||||||
color: #e6edf3;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 2fr 1fr 1fr;
|
|
||||||
grid-template-rows: 1fr 1fr;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
background: #3b4c5e;
|
|
||||||
border-radius: 14px;
|
|
||||||
padding: 18px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.25);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-light {
|
|
||||||
position: absolute;
|
|
||||||
top: 18px;
|
|
||||||
right: 18px;
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
border-radius: 50%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.green {
|
|
||||||
background: #3ad29f;
|
|
||||||
}
|
|
||||||
.yellow {
|
|
||||||
background: #f5c542;
|
|
||||||
}
|
|
||||||
.red {
|
|
||||||
background: #e05d5d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.green-text {
|
|
||||||
color: #3ad29f;
|
|
||||||
}
|
|
||||||
.yellow-text {
|
|
||||||
color: #f5c542;
|
|
||||||
}
|
|
||||||
.red-text {
|
|
||||||
color: #e05d5d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SERVER GRID */
|
|
||||||
.servers {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.server {
|
|
||||||
background: #44586d;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 12px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.server-name {
|
|
||||||
font-size: 15px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
opacity: 0.85;
|
|
||||||
font-weight: bold;
|
|
||||||
padding-right: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.server .status-light {
|
|
||||||
top: 12px;
|
|
||||||
right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metrics {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.7;
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MEETINGS */
|
|
||||||
.meetings-scroll {
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: calc(100% - 45px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting {
|
|
||||||
background: #44586d;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting.soon {
|
|
||||||
border: 2px solid #e05d5d;
|
|
||||||
box-shadow: 0 0 8px rgba(224, 93, 93, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting-name {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: bold;
|
|
||||||
opacity: 0.95;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting-badges {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
font-size: 9px;
|
|
||||||
padding: 2px 5px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-weight: bold;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-odoo {
|
|
||||||
background: #714b67;
|
|
||||||
color: #e6edf3;
|
|
||||||
}
|
|
||||||
.badge-brandize {
|
|
||||||
background: #1a6fa0;
|
|
||||||
color: #e6edf3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time {
|
|
||||||
font-size: 11px;
|
|
||||||
opacity: 0.65;
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODOS */
|
|
||||||
.todos-scroll {
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: calc(100% - 45px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo {
|
|
||||||
background: #44586d;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: 13px;
|
|
||||||
opacity: 0.9;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo.done-todo {
|
|
||||||
opacity: 0.5;
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-check {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 2px solid #8aa0b5;
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-check.checked {
|
|
||||||
background: #3ad29f;
|
|
||||||
border-color: #3ad29f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* LATEST SIGNUPS */
|
|
||||||
.signups-scroll {
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: calc(100% - 45px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-item {
|
|
||||||
background: #44586d;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-email {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
opacity: 0.95;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-meta {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
opacity: 0.7;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-tag {
|
|
||||||
background: #2f3e4e;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 1px 6px;
|
|
||||||
font-size: 10px;
|
|
||||||
color: #3ad29f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-server {
|
|
||||||
font-size: 11px;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CURRENT TESTS */
|
|
||||||
.test-box {
|
|
||||||
background: #44586d;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
|
||||||
padding: 3px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-label {
|
|
||||||
opacity: 0.6;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-value {
|
|
||||||
font-size: 11px;
|
|
||||||
text-align: right;
|
|
||||||
max-width: 65%;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-badge {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 1px 7px;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: bold;
|
|
||||||
background: #3ad29f33;
|
|
||||||
color: #3ad29f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-badge.dev {
|
|
||||||
background: #f5c54233;
|
|
||||||
color: #f5c542;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* LOADING / ERROR */
|
|
||||||
.loading {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 18px;
|
|
||||||
opacity: 0.5;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-msg {
|
|
||||||
color: #e05d5d;
|
|
||||||
font-size: 13px;
|
|
||||||
opacity: 0.85;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="dashboard" id="dashboard">
|
|
||||||
<div class="loading" style="grid-column: 1/-1; grid-row: 1/-1">
|
|
||||||
Loading dashboard…
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const WEBHOOK_URL =
|
|
||||||
"https://002-001-5dd6e535-4d1c-46bc-9bd9-42ad4bc5f082.odoo4projects.com/webhook/52781c84-9f12-4a61-96b2-9e1e3933148d";
|
|
||||||
const REFRESH_INTERVAL_MS = 60 * 1000; // refresh every 60 seconds
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Helpers
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
function mbToGb(mb) {
|
|
||||||
return (mb / 1024).toFixed(1);
|
|
||||||
}
|
|
||||||
function pct(free, total) {
|
|
||||||
return total > 0 ? (free / total) * 100 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function metricColor(freePct) {
|
|
||||||
if (freePct < 10) return "red";
|
|
||||||
if (freePct < 20) return "yellow";
|
|
||||||
return "green";
|
|
||||||
}
|
|
||||||
|
|
||||||
function worstColor(colors) {
|
|
||||||
if (colors.includes("red")) return "red";
|
|
||||||
if (colors.includes("yellow")) return "yellow";
|
|
||||||
return "green";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the wall-clock date/time directly from an ISO-8601 string that
|
|
||||||
// already carries an offset (e.g. "2026-03-31T10:00:00-03:00").
|
|
||||||
// We never let the browser re-interpret it into its own timezone.
|
|
||||||
function parseISOParts(dateTimeStr) {
|
|
||||||
// Matches: YYYY-MM-DDTHH:MM:SS(±HH:MM or Z)
|
|
||||||
const m = dateTimeStr.match(
|
|
||||||
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::\d{2})?/,
|
|
||||||
);
|
|
||||||
if (!m) return null;
|
|
||||||
return {
|
|
||||||
year: parseInt(m[1], 10),
|
|
||||||
month: parseInt(m[2], 10),
|
|
||||||
day: parseInt(m[3], 10),
|
|
||||||
hour: parseInt(m[4], 10),
|
|
||||||
minute: parseInt(m[5], 10),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatLocalTime(dateTimeStr) {
|
|
||||||
const p = parseISOParts(dateTimeStr);
|
|
||||||
if (!p) return "";
|
|
||||||
const h = String(p.hour).padStart(2, "0");
|
|
||||||
const m = String(p.minute).padStart(2, "0");
|
|
||||||
return `${h}:${m}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatLocalDate(dateTimeStr) {
|
|
||||||
const p = parseISOParts(dateTimeStr);
|
|
||||||
if (!p) return "";
|
|
||||||
// Build a UTC date just for the locale label so day/month names are correct
|
|
||||||
const d = new Date(Date.UTC(p.year, p.month - 1, p.day));
|
|
||||||
return d.toLocaleDateString([], {
|
|
||||||
weekday: "short",
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
timeZone: "UTC",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function isToday(dateTimeStr) {
|
|
||||||
const now = new Date();
|
|
||||||
const p = parseISOParts(dateTimeStr);
|
|
||||||
if (!p) return false;
|
|
||||||
return (
|
|
||||||
p.year === now.getFullYear() &&
|
|
||||||
p.month === now.getMonth() + 1 &&
|
|
||||||
p.day === now.getDate()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSoonOrOngoing(startStr, endStr) {
|
|
||||||
const now = new Date();
|
|
||||||
const start = new Date(startStr);
|
|
||||||
const end = new Date(endStr);
|
|
||||||
const diffMin = (start - now) / 60000;
|
|
||||||
if (now >= start && now <= end) return true;
|
|
||||||
if (diffMin >= 0 && diffMin <= 30) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Server cards
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
function buildServerCard(s) {
|
|
||||||
const ramPct = pct(s.free_ram, s.available_ram);
|
|
||||||
const hddPct = pct(s.free_hdd, s.available_hdd);
|
|
||||||
const ramColor = metricColor(ramPct);
|
|
||||||
const hddColor = metricColor(hddPct);
|
|
||||||
const lightColor = worstColor([ramColor, hddColor]);
|
|
||||||
|
|
||||||
const displayNames = {
|
|
||||||
saopaulo: "São Paulo",
|
|
||||||
manchester: "Manchester",
|
|
||||||
mumbai: "Mumbai",
|
|
||||||
sydney: "Sydney",
|
|
||||||
meppel: "Meppel",
|
|
||||||
boston: "Boston",
|
|
||||||
};
|
|
||||||
const displayName =
|
|
||||||
displayNames[s.Server] ||
|
|
||||||
s.Server.charAt(0).toUpperCase() + s.Server.slice(1);
|
|
||||||
|
|
||||||
return {
|
|
||||||
lightColor,
|
|
||||||
html: `
|
|
||||||
<div class="server">
|
|
||||||
<div class="server-name">${displayName}</div>
|
|
||||||
<div class="status-light ${lightColor}"></div>
|
|
||||||
<div class="metrics">
|
|
||||||
<div class="metric-row">
|
|
||||||
<span>RAM:</span>
|
|
||||||
<span class="${ramColor}-text">${ramPct.toFixed(0)}% free (${mbToGb(s.free_ram)} GB)</span>
|
|
||||||
</div>
|
|
||||||
<div class="metric-row">
|
|
||||||
<span>HDD:</span>
|
|
||||||
<span class="${hddColor}-text">${hddPct.toFixed(0)}% free (${mbToGb(s.free_hdd)} GB)</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Meetings
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
function parseMeetings(calEvents, calName) {
|
|
||||||
const seen = new Set();
|
|
||||||
const result = [];
|
|
||||||
for (const e of calEvents) {
|
|
||||||
const ev = e.json || e;
|
|
||||||
if (seen.has(ev.id)) continue;
|
|
||||||
seen.add(ev.id);
|
|
||||||
result.push({ ...ev, _calName: calName });
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMeetingCard(m) {
|
|
||||||
const startStr =
|
|
||||||
(m.start && (m.start.dateTime || m.start.date)) || "";
|
|
||||||
const endStr = (m.end && (m.end.dateTime || m.end.date)) || "";
|
|
||||||
const soon =
|
|
||||||
startStr && endStr
|
|
||||||
? isSoonOrOngoing(startStr, endStr)
|
|
||||||
: false;
|
|
||||||
const today = startStr ? isToday(startStr) : false;
|
|
||||||
|
|
||||||
const badges = (m._cals || [])
|
|
||||||
.map((c) => {
|
|
||||||
if (c === "ODOO4projects")
|
|
||||||
return `<span class="badge badge-odoo">ODOO4</span>`;
|
|
||||||
if (c === "Brandize")
|
|
||||||
return `<span class="badge badge-brandize">Brandize</span>`;
|
|
||||||
return "";
|
|
||||||
})
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
const dateLabel =
|
|
||||||
today || !startStr
|
|
||||||
? ""
|
|
||||||
: `<span style="font-size:10px;opacity:0.55;margin-right:4px;">${formatLocalDate(startStr)}</span>`;
|
|
||||||
|
|
||||||
const timeLabel =
|
|
||||||
startStr && endStr
|
|
||||||
? `${formatLocalTime(startStr)} – ${formatLocalTime(endStr)}`
|
|
||||||
: "";
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div class="meeting${soon ? " soon" : ""}">
|
|
||||||
<div class="meeting-header">
|
|
||||||
<div class="meeting-name">${m.summary || "Meeting"}</div>
|
|
||||||
<div class="meeting-badges">${badges}</div>
|
|
||||||
</div>
|
|
||||||
<div class="time">${dateLabel}${timeLabel}</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Todos
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
function buildTodoItem(t) {
|
|
||||||
const done = !!t.done;
|
|
||||||
const checkInner = done
|
|
||||||
? `<svg width="10" height="10" viewBox="0 0 10 10"><polyline points="1.5,5 4,7.5 8.5,2.5" stroke="#fff" stroke-width="1.8" fill="none" stroke-linecap="round"/></svg>`
|
|
||||||
: "";
|
|
||||||
return `
|
|
||||||
<div class="todo${done ? " done-todo" : ""}">
|
|
||||||
<div class="todo-check${done ? " checked" : ""}">${checkInner}</div>
|
|
||||||
<span>${t.name || ""}</span>
|
|
||||||
${t.Minutes != null ? `<span style="margin-left:auto;opacity:0.55;font-size:12px;flex-shrink:0;">${t.Minutes}</span>` : ""}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Latest Signup item
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
function buildSignupItem(ct) {
|
|
||||||
if (!ct) return "";
|
|
||||||
const tagBadges = (ct.tags || "")
|
|
||||||
.split(",")
|
|
||||||
.map((t) => t.trim())
|
|
||||||
.filter(Boolean)
|
|
||||||
.map((t) => `<span class="signup-tag">${t}</span>`)
|
|
||||||
.join("");
|
|
||||||
return `
|
|
||||||
<div class="signup-item">
|
|
||||||
<div class="signup-email">${ct.email || ""}</div>
|
|
||||||
<div class="signup-meta">
|
|
||||||
<span class="signup-server">🖥 ${ct.server || ""}</span>
|
|
||||||
${tagBadges}
|
|
||||||
<span style="opacity:0.5;font-size:10px;">${ct.status || ""}</span>
|
|
||||||
</div>
|
|
||||||
<div style="margin-top:4px;opacity:0.55;font-size:10px;">
|
|
||||||
${ct.Domains ? ct.Domains : ""}${ct.expires ? " · expires " + ct.expires : ""}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildSignupItems(raw) {
|
|
||||||
if (!raw) return { html: "", count: 0 };
|
|
||||||
const items = Array.isArray(raw) ? raw : [raw];
|
|
||||||
const html = items.map(buildSignupItem).join("");
|
|
||||||
return { html, count: items.length };
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Render
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
function render(data) {
|
|
||||||
const serverStatusRaw = data["Server Status"] || [];
|
|
||||||
const calOdoo = data["Calender ODOO4projects"] || [];
|
|
||||||
const calBrandize = data["Calender Brandize"] || [];
|
|
||||||
const todos = data["Todos"] || [];
|
|
||||||
const currentTestsRaw = data["Current Tests"] || null;
|
|
||||||
|
|
||||||
// Deduplicate servers by Server key
|
|
||||||
const serverMap = {};
|
|
||||||
for (const s of serverStatusRaw) {
|
|
||||||
if (!serverMap[s.Server]) serverMap[s.Server] = s;
|
|
||||||
}
|
|
||||||
const servers = Object.values(serverMap);
|
|
||||||
|
|
||||||
// Server cards
|
|
||||||
const serverCards = servers.map(buildServerCard);
|
|
||||||
const overallColor = worstColor(
|
|
||||||
serverCards.map((c) => c.lightColor),
|
|
||||||
);
|
|
||||||
const serverCardsHtml = serverCards.map((c) => c.html).join("");
|
|
||||||
|
|
||||||
// Meetings – merge & deduplicate across both calendars
|
|
||||||
const odooMeetings = parseMeetings(calOdoo, "ODOO4projects");
|
|
||||||
const brandizeMeetings = parseMeetings(calBrandize, "Brandize");
|
|
||||||
const allMeetings = [...odooMeetings, ...brandizeMeetings];
|
|
||||||
|
|
||||||
const meetingById = {};
|
|
||||||
for (const m of allMeetings) {
|
|
||||||
if (!meetingById[m.id]) {
|
|
||||||
meetingById[m.id] = { ...m, _cals: [m._calName] };
|
|
||||||
} else if (!meetingById[m.id]._cals.includes(m._calName)) {
|
|
||||||
meetingById[m.id]._cals.push(m._calName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const dedupedMeetings = Object.values(meetingById).sort(
|
|
||||||
(a, b) => {
|
|
||||||
const aStart =
|
|
||||||
(a.start && (a.start.dateTime || a.start.date)) ||
|
|
||||||
"";
|
|
||||||
const bStart =
|
|
||||||
(b.start && (b.start.dateTime || b.start.date)) ||
|
|
||||||
"";
|
|
||||||
return new Date(aStart) - new Date(bStart);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const meetingsHtml = dedupedMeetings
|
|
||||||
.map(buildMeetingCard)
|
|
||||||
.join("");
|
|
||||||
const todosHtml = todos.map(buildTodoItem).join("");
|
|
||||||
const totalMinutes = todos.reduce(
|
|
||||||
(sum, t) => sum + (t.Minutes || 0),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const totalHours = (totalMinutes / 60)
|
|
||||||
.toFixed(1)
|
|
||||||
.replace(/\.0$/, "");
|
|
||||||
const signups = buildSignupItems(currentTestsRaw);
|
|
||||||
|
|
||||||
document.getElementById("dashboard").innerHTML = `
|
|
||||||
|
|
||||||
<!-- SERVER STATUS -->
|
|
||||||
<div class="box" style="grid-column:1; grid-row:1/3;">
|
|
||||||
<div class="title">Server Status</div>
|
|
||||||
<div class="status-light ${overallColor}"></div>
|
|
||||||
<div class="servers">
|
|
||||||
${serverCardsHtml}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- TODAY'S MEETINGS -->
|
|
||||||
<div class="box" style="grid-column:2; grid-row:1;">
|
|
||||||
<div class="title">Meetings</div>
|
|
||||||
<div class="meetings-scroll">
|
|
||||||
${meetingsHtml || '<div style="opacity:0.4;font-size:13px;">No meetings</div>'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- LATEST SIGNUPS -->
|
|
||||||
<div class="box" style="grid-column:3; grid-row:1/3;">
|
|
||||||
<div class="title">Latest Signups <span style="font-size:13px;font-weight:normal;opacity:0.6;background:#2f3e4e;border-radius:10px;padding:1px 9px;margin-left:6px;">${signups.count}</span></div>
|
|
||||||
<div class="signups-scroll">
|
|
||||||
${signups.html || '<div style="opacity:0.4;font-size:13px;">No signups</div>'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- TODOS -->
|
|
||||||
<div class="box" style="grid-column:2; grid-row:2;">
|
|
||||||
<div class="title" style="display:flex;align-items:center;">Today's Todos<span style="margin-left:auto;font-size:13px;font-weight:normal;opacity:0.6;background:#2f3e4e;border-radius:10px;padding:1px 9px;">${totalHours}h</span></div>
|
|
||||||
<div class="todos-scroll">
|
|
||||||
${todosHtml || '<div style="opacity:0.4;font-size:13px;">No todos</div>'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Fetch & refresh loop
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
async function loadData() {
|
|
||||||
try {
|
|
||||||
const res = await fetch(WEBHOOK_URL);
|
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
||||||
const data = await res.json();
|
|
||||||
render(data);
|
|
||||||
} catch (err) {
|
|
||||||
document.getElementById("dashboard").innerHTML = `
|
|
||||||
<div class="error-msg" style="grid-column:1/-1;grid-row:1/-1;padding:40px;">
|
|
||||||
⚠ Failed to load dashboard data: ${err.message}<br>
|
|
||||||
<span style="font-size:11px;opacity:0.6;">Retrying in ${REFRESH_INTERVAL_MS / 1000}s…</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadData();
|
|
||||||
setInterval(loadData, REFRESH_INTERVAL_MS);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
+114
@@ -0,0 +1,114 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,-1152,-256)">
|
||||||
|
<rect id="Icons" x="0" y="0" width="1280" height="800" style="fill:none;"/>
|
||||||
|
<g id="Icons1" serif:id="Icons">
|
||||||
|
<g id="Strike">
|
||||||
|
</g>
|
||||||
|
<g id="H1">
|
||||||
|
</g>
|
||||||
|
<g id="H2">
|
||||||
|
</g>
|
||||||
|
<g id="H3">
|
||||||
|
</g>
|
||||||
|
<g id="list-ul">
|
||||||
|
</g>
|
||||||
|
<g id="hamburger-1">
|
||||||
|
</g>
|
||||||
|
<g id="hamburger-2">
|
||||||
|
</g>
|
||||||
|
<g id="list-ol">
|
||||||
|
</g>
|
||||||
|
<g id="list-task">
|
||||||
|
</g>
|
||||||
|
<g id="trash">
|
||||||
|
</g>
|
||||||
|
<g id="vertical-menu">
|
||||||
|
</g>
|
||||||
|
<g id="horizontal-menu">
|
||||||
|
</g>
|
||||||
|
<g id="sidebar-2">
|
||||||
|
</g>
|
||||||
|
<g id="Pen">
|
||||||
|
</g>
|
||||||
|
<g id="Pen1" serif:id="Pen">
|
||||||
|
</g>
|
||||||
|
<g id="clock">
|
||||||
|
</g>
|
||||||
|
<g id="external-link">
|
||||||
|
</g>
|
||||||
|
<g id="hr">
|
||||||
|
</g>
|
||||||
|
<g id="info">
|
||||||
|
</g>
|
||||||
|
<g id="warning">
|
||||||
|
</g>
|
||||||
|
<g id="plus-circle">
|
||||||
|
</g>
|
||||||
|
<g id="minus-circle">
|
||||||
|
</g>
|
||||||
|
<g id="vue">
|
||||||
|
</g>
|
||||||
|
<g id="cog">
|
||||||
|
</g>
|
||||||
|
<g id="logo">
|
||||||
|
</g>
|
||||||
|
<g id="radio-check">
|
||||||
|
</g>
|
||||||
|
<g id="eye-slash">
|
||||||
|
</g>
|
||||||
|
<g id="eye">
|
||||||
|
</g>
|
||||||
|
<g id="toggle-off">
|
||||||
|
</g>
|
||||||
|
<g id="shredder">
|
||||||
|
</g>
|
||||||
|
<g id="spinner--loading--dots-" serif:id="spinner [loading, dots]">
|
||||||
|
</g>
|
||||||
|
<g id="react">
|
||||||
|
</g>
|
||||||
|
<g id="check-selected">
|
||||||
|
</g>
|
||||||
|
<g id="turn-off">
|
||||||
|
</g>
|
||||||
|
<g id="code-block">
|
||||||
|
</g>
|
||||||
|
<g id="user">
|
||||||
|
</g>
|
||||||
|
<g id="coffee-bean">
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.638317,0.368532,-0.368532,0.638317,785.021,-208.975)">
|
||||||
|
<g id="coffee-beans">
|
||||||
|
<g id="coffee-bean1" serif:id="coffee-bean">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="coffee-bean-filled" transform="matrix(0.866025,0.5,-0.5,0.866025,717.879,-387.292)">
|
||||||
|
<g transform="matrix(1,0,0,1,0,-0.699553)">
|
||||||
|
<path d="M737.673,328.231C738.494,328.056 739.334,328.427 739.757,329.152C739.955,329.463 740.106,329.722 740.106,329.722C740.106,329.722 745.206,338.581 739.429,352.782C737.079,358.559 736.492,366.083 738.435,371.679C738.697,372.426 738.482,373.258 737.89,373.784C737.298,374.31 736.447,374.426 735.735,374.077C730.192,371.375 722.028,365.058 722.021,352C722.015,340.226 728.812,330.279 737.673,328.231Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(-1,0,0,-1,1483.03,703.293)">
|
||||||
|
<path d="M737.609,328.246C738.465,328.06 739.344,328.446 739.785,329.203C739.97,329.49 740.106,329.722 740.106,329.722C740.106,329.722 745.206,338.581 739.429,352.782C737.1,358.507 736.503,365.948 738.383,371.527C738.646,372.304 738.415,373.164 737.796,373.703C737.177,374.243 736.294,374.356 735.56,373.989C730.02,371.241 722.028,364.92 722.021,352C722.016,340.255 728.779,330.328 737.609,328.246Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.638317,0.368532,-0.368532,0.638317,913.062,-208.975)">
|
||||||
|
<g id="coffee-beans-filled">
|
||||||
|
<g id="coffee-bean2" serif:id="coffee-bean">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="clipboard">
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,128.011,1.35415)">
|
||||||
|
<g id="clipboard-paste">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="clipboard-copy">
|
||||||
|
</g>
|
||||||
|
<g id="Layer1">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,224 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
fill="#000000"
|
||||||
|
width="800px"
|
||||||
|
height="800px"
|
||||||
|
viewBox="0 0 64 64"
|
||||||
|
version="1.1"
|
||||||
|
xml:space="preserve"
|
||||||
|
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"
|
||||||
|
id="svg6"
|
||||||
|
sodipodi:docname="bean_dark_Roast.svg"
|
||||||
|
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:serif="http://www.serif.com/"><defs
|
||||||
|
id="defs6" /><sodipodi:namedview
|
||||||
|
id="namedview6"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:zoom="1.075"
|
||||||
|
inkscape:cx="400"
|
||||||
|
inkscape:cy="400"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1129"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg6" />
|
||||||
|
<g
|
||||||
|
transform="matrix(1,0,0,1,-1152,-256)"
|
||||||
|
id="g6">
|
||||||
|
<rect
|
||||||
|
id="Icons"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1280"
|
||||||
|
height="800"
|
||||||
|
style="fill:none;" />
|
||||||
|
<g
|
||||||
|
id="Icons1"
|
||||||
|
serif:id="Icons">
|
||||||
|
<g
|
||||||
|
id="Strike">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="H1">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="H2">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="H3">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="list-ul">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="hamburger-1">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="hamburger-2">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="list-ol">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="list-task">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="trash">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="vertical-menu">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="horizontal-menu">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="sidebar-2">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Pen">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Pen1"
|
||||||
|
serif:id="Pen">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="clock">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="external-link">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="hr">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="info">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="warning">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="plus-circle">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="minus-circle">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="vue">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="cog">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="logo">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="radio-check">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="eye-slash">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="eye">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="toggle-off">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="shredder">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="spinner--loading--dots-"
|
||||||
|
serif:id="spinner [loading, dots]">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="react">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="check-selected">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="turn-off">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="code-block">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="user">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="coffee-bean">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0.638317,0.368532,-0.368532,0.638317,785.021,-208.975)"
|
||||||
|
id="g1">
|
||||||
|
<g
|
||||||
|
id="coffee-beans">
|
||||||
|
<g
|
||||||
|
id="coffee-bean1"
|
||||||
|
serif:id="coffee-bean">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="coffee-bean-filled"
|
||||||
|
transform="matrix(0.866025,0.5,-0.5,0.866025,717.879,-387.292)">
|
||||||
|
<g
|
||||||
|
transform="matrix(1,0,0,1,0,-0.699553)"
|
||||||
|
id="g2">
|
||||||
|
<path
|
||||||
|
d="M737.673,328.231C738.494,328.056 739.334,328.427 739.757,329.152C739.955,329.463 740.106,329.722 740.106,329.722C740.106,329.722 745.206,338.581 739.429,352.782C737.079,358.559 736.492,366.083 738.435,371.679C738.697,372.426 738.482,373.258 737.89,373.784C737.298,374.31 736.447,374.426 735.735,374.077C730.192,371.375 722.028,365.058 722.021,352C722.015,340.226 728.812,330.279 737.673,328.231Z"
|
||||||
|
id="path1"
|
||||||
|
style="fill:#3e2723;fill-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(-1,0,0,-1,1483.03,703.293)"
|
||||||
|
id="g3">
|
||||||
|
<path
|
||||||
|
d="M737.609,328.246C738.465,328.06 739.344,328.446 739.785,329.203C739.97,329.49 740.106,329.722 740.106,329.722C740.106,329.722 745.206,338.581 739.429,352.782C737.1,358.507 736.503,365.948 738.383,371.527C738.646,372.304 738.415,373.164 737.796,373.703C737.177,374.243 736.294,374.356 735.56,373.989C730.02,371.241 722.028,364.92 722.021,352C722.016,340.255 728.779,330.328 737.609,328.246Z"
|
||||||
|
id="path2"
|
||||||
|
style="stroke:#3e2723;stroke-opacity:1;fill:#3e2723;fill-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0.638317,0.368532,-0.368532,0.638317,913.062,-208.975)"
|
||||||
|
id="g4">
|
||||||
|
<g
|
||||||
|
id="coffee-beans-filled">
|
||||||
|
<g
|
||||||
|
id="coffee-bean2"
|
||||||
|
serif:id="coffee-bean">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="clipboard">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(1,0,0,1,128.011,1.35415)"
|
||||||
|
id="g5">
|
||||||
|
<g
|
||||||
|
id="clipboard-paste">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="clipboard-copy">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Layer1">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,224 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
fill="#000000"
|
||||||
|
width="800px"
|
||||||
|
height="800px"
|
||||||
|
viewBox="0 0 64 64"
|
||||||
|
version="1.1"
|
||||||
|
xml:space="preserve"
|
||||||
|
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"
|
||||||
|
id="svg6"
|
||||||
|
sodipodi:docname="bean_light_brown.svg"
|
||||||
|
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:serif="http://www.serif.com/"><defs
|
||||||
|
id="defs6" /><sodipodi:namedview
|
||||||
|
id="namedview6"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:zoom="1.075"
|
||||||
|
inkscape:cx="400"
|
||||||
|
inkscape:cy="400"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1129"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg6" />
|
||||||
|
<g
|
||||||
|
transform="matrix(1,0,0,1,-1152,-256)"
|
||||||
|
id="g6">
|
||||||
|
<rect
|
||||||
|
id="Icons"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1280"
|
||||||
|
height="800"
|
||||||
|
style="fill:none;" />
|
||||||
|
<g
|
||||||
|
id="Icons1"
|
||||||
|
serif:id="Icons">
|
||||||
|
<g
|
||||||
|
id="Strike">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="H1">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="H2">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="H3">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="list-ul">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="hamburger-1">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="hamburger-2">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="list-ol">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="list-task">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="trash">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="vertical-menu">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="horizontal-menu">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="sidebar-2">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Pen">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Pen1"
|
||||||
|
serif:id="Pen">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="clock">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="external-link">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="hr">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="info">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="warning">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="plus-circle">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="minus-circle">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="vue">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="cog">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="logo">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="radio-check">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="eye-slash">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="eye">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="toggle-off">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="shredder">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="spinner--loading--dots-"
|
||||||
|
serif:id="spinner [loading, dots]">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="react">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="check-selected">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="turn-off">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="code-block">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="user">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="coffee-bean">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0.638317,0.368532,-0.368532,0.638317,785.021,-208.975)"
|
||||||
|
id="g1">
|
||||||
|
<g
|
||||||
|
id="coffee-beans">
|
||||||
|
<g
|
||||||
|
id="coffee-bean1"
|
||||||
|
serif:id="coffee-bean">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="coffee-bean-filled"
|
||||||
|
transform="matrix(0.866025,0.5,-0.5,0.866025,717.879,-387.292)">
|
||||||
|
<g
|
||||||
|
transform="matrix(1,0,0,1,0,-0.699553)"
|
||||||
|
id="g2">
|
||||||
|
<path
|
||||||
|
d="M737.673,328.231C738.494,328.056 739.334,328.427 739.757,329.152C739.955,329.463 740.106,329.722 740.106,329.722C740.106,329.722 745.206,338.581 739.429,352.782C737.079,358.559 736.492,366.083 738.435,371.679C738.697,372.426 738.482,373.258 737.89,373.784C737.298,374.31 736.447,374.426 735.735,374.077C730.192,371.375 722.028,365.058 722.021,352C722.015,340.226 728.812,330.279 737.673,328.231Z"
|
||||||
|
id="path1"
|
||||||
|
style="fill:#5d4037;fill-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(-1,0,0,-1,1483.03,703.293)"
|
||||||
|
id="g3">
|
||||||
|
<path
|
||||||
|
d="M737.609,328.246C738.465,328.06 739.344,328.446 739.785,329.203C739.97,329.49 740.106,329.722 740.106,329.722C740.106,329.722 745.206,338.581 739.429,352.782C737.1,358.507 736.503,365.948 738.383,371.527C738.646,372.304 738.415,373.164 737.796,373.703C737.177,374.243 736.294,374.356 735.56,373.989C730.02,371.241 722.028,364.92 722.021,352C722.016,340.255 728.779,330.328 737.609,328.246Z"
|
||||||
|
id="path2"
|
||||||
|
style="stroke:#5d4037;stroke-opacity:1;fill:#5d4037;fill-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0.638317,0.368532,-0.368532,0.638317,913.062,-208.975)"
|
||||||
|
id="g4">
|
||||||
|
<g
|
||||||
|
id="coffee-beans-filled">
|
||||||
|
<g
|
||||||
|
id="coffee-bean2"
|
||||||
|
serif:id="coffee-bean">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="clipboard">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(1,0,0,1,128.011,1.35415)"
|
||||||
|
id="g5">
|
||||||
|
<g
|
||||||
|
id="clipboard-paste">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="clipboard-copy">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Layer1">
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
+116
@@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="124.8711mm"
|
||||||
|
height="31.440542mm"
|
||||||
|
viewBox="0 0 124.8711 31.440542"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||||
|
sodipodi:docname="logo.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="0.70021044"
|
||||||
|
inkscape:cx="329.90082"
|
||||||
|
inkscape:cy="345.61039"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1046"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Ebene 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-17.727083,-56.885417)">
|
||||||
|
<g
|
||||||
|
id="g5"
|
||||||
|
transform="matrix(0.26458333,0,0,0.26458333,-67.310852,25.675979)">
|
||||||
|
<text
|
||||||
|
id="text5"
|
||||||
|
xml:space="preserve"
|
||||||
|
transform="matrix(1.3333333,0,0,1.3333333,316.00667,186.29426)"><tspan
|
||||||
|
id="tspan5"
|
||||||
|
style="font-variant:normal;font-weight:700;font-size:65.991px;font-family:'Noto Sans';writing-mode:lr-tb;fill:#3e2723;fill-opacity:1;fill-rule:nonzero;stroke:#3e2723;stroke-width:2.19965;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="0"
|
||||||
|
y="0">my</tspan></text>
|
||||||
|
<text
|
||||||
|
id="text6"
|
||||||
|
xml:space="preserve"
|
||||||
|
transform="matrix(1.3333333,0,0,1.3333333,452.48533,186.29426)"><tspan
|
||||||
|
id="tspan6"
|
||||||
|
style="font-variant:normal;font-weight:700;font-size:65.991px;font-family:'Noto Sans';writing-mode:lr-tb;fill:#e65100;fill-opacity:1;fill-rule:nonzero;stroke:#e65100;stroke-width:2.19965;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="0"
|
||||||
|
y="0">-</tspan></text>
|
||||||
|
<text
|
||||||
|
id="text7"
|
||||||
|
xml:space="preserve"
|
||||||
|
transform="matrix(1.3333333,0,0,1.3333333,480.79333,186.29426)"><tspan
|
||||||
|
id="tspan7"
|
||||||
|
style="font-variant:normal;font-weight:700;font-size:65.991px;font-family:'Noto Sans';writing-mode:lr-tb;fill:#3e2723;fill-opacity:1;fill-rule:nonzero;stroke:#3e2723;stroke-width:2.19965;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="0"
|
||||||
|
y="0">biz</tspan></text>
|
||||||
|
<text
|
||||||
|
id="text8"
|
||||||
|
xml:space="preserve"
|
||||||
|
transform="matrix(1.3333333,0,0,1.3333333,606.236,186.29426)"><tspan
|
||||||
|
id="tspan8"
|
||||||
|
style="font-variant:normal;font-weight:700;font-size:65.991px;font-family:'Noto Sans';writing-mode:lr-tb;fill:#e65100;fill-opacity:1;fill-rule:nonzero;stroke:#e65100;stroke-width:2.19965;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="0"
|
||||||
|
y="0">.app</tspan></text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g8"
|
||||||
|
transform="matrix(0.26458333,0,0,0.26458333,-67.310852,25.675979)">
|
||||||
|
<path
|
||||||
|
id="path8"
|
||||||
|
d="M 249.449,279.212 H 391.181"
|
||||||
|
style="fill:none;stroke:#e65100;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,0,595.27559)" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g9"
|
||||||
|
transform="matrix(0.26458333,0,0,0.26458333,-67.310852,25.675979)">
|
||||||
|
<path
|
||||||
|
id="path9"
|
||||||
|
d="M 442.205,279.212 H 583.937"
|
||||||
|
style="fill:none;stroke:#e65100;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,0,595.27559)" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g10"
|
||||||
|
transform="matrix(0.26458333,0,0,0.26458333,-67.310852,25.675979)">
|
||||||
|
<path
|
||||||
|
id="path10"
|
||||||
|
d="m 420.661,289.332 c 0.369,-0.114 0.596,-0.454 0.596,-0.822 0.028,-0.171 0.028,-0.284 0.028,-0.284 0,0 -0.028,-4.535 -5.386,-8.702 -2.182,-1.701 -4.053,-4.451 -4.564,-7.03 -0.056,-0.34 -0.311,-0.624 -0.652,-0.681 -0.368,-0.085 -0.708,0.057 -0.907,0.369 -1.53,2.268 -3.259,6.491 -0.368,11.48 2.608,4.536 7.427,6.832 11.253,5.67 z"
|
||||||
|
style="fill:#3e2723;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,0,595.27559)" />
|
||||||
|
<path
|
||||||
|
id="path11"
|
||||||
|
d="m 413.121,269.376 c -0.368,0.141 -0.623,0.482 -0.623,0.85 0,0.17 0,0.284 0,0.284 0,0 0,4.535 5.357,8.702 2.154,1.672 4.054,4.394 4.564,6.945 0.057,0.368 0.34,0.623 0.709,0.708 0.34,0.057 0.708,-0.085 0.907,-0.396 1.53,-2.268 3.174,-6.463 0.34,-11.424 -2.608,-4.507 -7.399,-6.803 -11.254,-5.669 z"
|
||||||
|
style="fill:#3e2723;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,0,595.27559)" />
|
||||||
|
<path
|
||||||
|
id="path12"
|
||||||
|
d="m 413.121,269.376 c -0.368,0.141 -0.623,0.482 -0.623,0.85 0,0.17 0,0.284 0,0.284 0,0 0,4.535 5.357,8.702 2.154,1.672 4.054,4.394 4.564,6.945 0.057,0.368 0.34,0.623 0.709,0.708 0.34,0.057 0.708,-0.085 0.907,-0.396 1.53,-2.268 3.174,-6.463 0.34,-11.424 -2.608,-4.507 -7.399,-6.803 -11.254,-5.669 z"
|
||||||
|
style="fill:none;stroke:#3e2723;stroke-width:0.4428;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,0,595.27559)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.8 KiB |
+3673
-2502
File diff suppressed because it is too large
Load Diff
Generated
-1025
File diff suppressed because it is too large
Load Diff
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "ngo",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build:css": "tailwindcss -i src/input.css -o assets/site.css --minify",
|
|
||||||
"watch:css": "tailwindcss -i src/input.css -o assets/site.css --watch",
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git@git.odoo4projects.com:Oliver/NGO.git"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"type": "commonjs",
|
|
||||||
"devDependencies": {
|
|
||||||
"tailwindcss": "^3.4.17"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"headline": "How a regional NGO cut grant reporting time by 70%",
|
||||||
|
"vertical": "NGO",
|
||||||
|
"date": "2025-06-12",
|
||||||
|
"shortText": "Donor reconciliation and project cost allocation moved into one workflow, returning 15 hours a week to mission work instead of admin.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "https://ngo.my-biz.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headline": "Why investors love clean Odoo data rooms",
|
||||||
|
"vertical": "Startups",
|
||||||
|
"date": "2025-05-28",
|
||||||
|
"shortText": "Founders shared how structured finance and CRM data reduced scramble during due diligence.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "https://startups.my-biz.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headline": "Real-time BoM costing saved a factory $40k",
|
||||||
|
"vertical": "Manufacturing",
|
||||||
|
"date": "2025-04-21",
|
||||||
|
"shortText": "Live material cost tracking helped the team reprice before margin loss became irreversible.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1565793298595-6a879b1d9492?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "https://manufacturing.my-biz.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headline": "Food cost under control before it hit the P&L",
|
||||||
|
"vertical": "Restaurants",
|
||||||
|
"date": "2025-03-14",
|
||||||
|
"shortText": "Linking POS data to supplier invoices exposed a 12% creep in week one instead of month three.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "https://restaurants.my-biz.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headline": "Impact dashboards boards helped trustees decide faster",
|
||||||
|
"vertical": "NGO",
|
||||||
|
"date": "2025-02-10",
|
||||||
|
"shortText": "Program leads and finance teams aligned on one dashboard instead of emailing static reports back and forth.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "https://ngo.my-biz.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headline": "Startup founders replaced five admin tools with one stack",
|
||||||
|
"vertical": "Startups",
|
||||||
|
"date": "2025-01-24",
|
||||||
|
"shortText": "Sales, invoicing, and project workflows moved into one system and reduced duplicate entry across the team.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1552664730-d307ca884978?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "https://startups.my-biz.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headline": "Maintenance planning cut line stoppages in week three",
|
||||||
|
"vertical": "Manufacturing",
|
||||||
|
"date": "2024-12-16",
|
||||||
|
"shortText": "Production teams connected maintenance schedules with inventory and planning data before failures became emergencies.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1581092580497-e0d23cbdf1dc?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "https://manufacturing.my-biz.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headline": "Restaurant groups finally saw payroll and stock in one view",
|
||||||
|
"vertical": "Restaurants",
|
||||||
|
"date": "2024-11-30",
|
||||||
|
"shortText": "Managers connected staffing, purchasing, and daily sales to spot margin leaks earlier each week.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1552566626-52f8b828add9?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "https://restaurants.my-biz.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headline": "Automation teams improved agent accuracy with structured records",
|
||||||
|
"vertical": "Agentic Businesses",
|
||||||
|
"date": "2024-10-18",
|
||||||
|
"shortText": "AI workflows performed more reliably once customer, ops, and billing data shared the same foundation.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "https://agentic.my-biz.app"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headline": "Cross-border teams chose regional servers without changing workflows",
|
||||||
|
"vertical": "Infrastructure",
|
||||||
|
"date": "2024-09-06",
|
||||||
|
"shortText": "Distributed companies matched server location to their needs while keeping finance, CRM, and operations fully connected.",
|
||||||
|
"image": "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=900&q=80&auto=format&fit=crop",
|
||||||
|
"link": "#pricing"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
body {
|
|
||||||
@apply font-brand;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lang-selector {
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#lang-selector:focus,
|
|
||||||
#lang-selector:focus-visible {
|
|
||||||
color: #201824;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Blog post modal animations ───────────────────────── */
|
|
||||||
|
|
||||||
@keyframes modal-slide-in {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.95) translateY(20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1) translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes modal-fade-in {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-animate-box {
|
|
||||||
animation: modal-slide-in 0.3s cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-animate-backdrop {
|
|
||||||
animation: modal-fade-in 0.2s ease both;
|
|
||||||
}
|
|
||||||
+1630
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
module.exports = {
|
|
||||||
content: ["./index.html"],
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
"brand-ink": "#201824",
|
|
||||||
"brand-primary": "#603F57",
|
|
||||||
"brand-accent": "#F762B4",
|
|
||||||
"brand-sunrise": "#F8B84A",
|
|
||||||
"brand-emerald": "#39B982",
|
|
||||||
"brand-snow": "#F5F2F7",
|
|
||||||
"brand-smoke": "#E5DCE8",
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
brand: ["system-ui", "-apple-system", "Segoe UI", "sans-serif"],
|
|
||||||
},
|
|
||||||
boxShadow: {
|
|
||||||
brand: "0 20px 60px rgba(32, 24, 36, 0.15)",
|
|
||||||
},
|
|
||||||
backgroundImage: {
|
|
||||||
"hero-gradient":
|
|
||||||
"radial-gradient(circle at top left, rgba(247,98,180,0.35), rgba(32,24,36,0.95))",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user