Portfolio: ZockerAnstalt, Buchhaltung und Command Center aufgenommen
Aus zwei Projekten werden fuenf. Alle drei neuen laufen wirklich, die Adressen wurden vorher einzeln geprueft: zockeranstalt.dogfather-universe.com Netcup, via Caddy buchhaltung.vans-diy-bastelbedarf.com Netcup, via Caddy analyse.dogfather-universe.com Cloudflare Worker (kein via) Die Vorschaubilder sind echte Aufnahmen vom 24.08.2026, aufgenommen im selben Mass wie die bestehenden (1200x750), damit in der Liste nichts aus der Reihe faellt. Zwei Aufnahmen sind bewusst nicht unbearbeitet, und das steht auch fuer die Besucher auf der Seite -- nicht nur im Code: - Die Buchhaltung zeigt nur die Anmeldung. Sie bekommt deshalb auch KEINEN "Live ansehen"-Knopf: Ein Knopf, der bloss auf eine Anmeldemaske fuehrt, ist ein leeres Versprechen. Ein Test haelt das fest, damit es niemand spaeter "der Vollstaendigkeit halber" einbaut. - Beim Command Center sind die Gesichter unkenntlich gemacht. Die Seite gehoert dem Team, nicht mir, und niemand hat zugestimmt, sein Bild im Portfolio zu zeigen. Der Weichzeichner wurde vor der Aufnahme im Browser gesetzt, nicht nachtraeglich ins Bild gemalt. Zwei neue Schildfarben. Aqua traegt Schrift problemlos (11,5:1 gemessen). Indigo NICHT: Prism Indigo ist die einzige Farbe der Palette, die als Text durchfaellt (3,25--4,29:1) -- das Schild nutzt es deshalb nur als Flaeche und Kante, die Schrift bleibt Chrome Silver (9,8:1). Texte in allen fuenf Sprachen, inklusive Schweizerdeutsch. Ueberschrift, Einleitung und Schlusssatz sagen nicht mehr "zwei" bzw. "beide" -- und zwar sowohl im Woerterbuch ALS AUCH im deutschen Rueckfalltext im HTML. Nur die JS-Datei zu aendern haette gereicht, damit es in vier Sprachen stimmt und ausgerechnet auf Deutsch falsch bleibt. Drei Fehler steckten wieder in der Pruefung, nicht auf der Seite: - Sie meldete drei Vorschaubilder als "nicht geladen". Die tragen loading="lazy" -- der Test hatte schlicht nie hingesehen. - Sie zaehlte Navigations- und Markentexte mit. Die liegen aber in wd-core.js, gelten fuer alle Seiten und werden von server/pruefe-webdesign-i18n.mjs abgedeckt. - Ihre "kein Zwei mehr"-Suche haette auch Projekttexte getroffen, in denen das Wort voellig zurecht steht. Geprueft wird jetzt das Ergebnis statt des Woerterbuchs: Die Seite wird wirklich auf jede der fuenf Sprachen umgeschaltet, plus eine Gegenprobe, dass die Umschaltung ueberhaupt etwas tut. Geprueft: 198 Pruefungen gruen (Portfolio 15, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Angebot 54, Abbruch 42). Die vier Meldungen aus server/pruefe-webdesign-i18n.mjs betreffen verwaltung.html und zwei fehlende Woerterbuecher -- Altbestand, keine dieser Dateien wurde hier angefasst. Co-Authored-By: Claude Opus 5 <[email protected]>
@@ -615,6 +615,13 @@ a.wd-karte:hover, .wd-karte--klickbar:hover {
|
||||
.wd-tag--lila { background: rgb(var(--wd-lila-rgb) / .12); border-color: rgb(var(--wd-lila-rgb) / .34); color: var(--wd-lila); }
|
||||
.wd-tag--gold { background: rgb(var(--wd-gold-rgb) / .12); border-color: rgb(var(--wd-gold-rgb) / .34); color: var(--wd-gold); }
|
||||
.wd-tag--ok { background: rgb(var(--wd-ok-rgb) / .12); border-color: rgb(var(--wd-ok-rgb) / .34); color: var(--wd-ok); }
|
||||
/* Aqua fuer die Arcade. Als Schriftfarbe unbedenklich: Hyper Aqua liegt
|
||||
auf allen vier dunklen Flaechen zwischen 12,4:1 und 19,8:1. */
|
||||
.wd-tag--aqua { background: rgb(var(--wd-aqua-rgb) / .12); border-color: rgb(var(--wd-aqua-rgb) / .34); color: rgb(var(--wd-aqua-rgb)); }
|
||||
/* Indigo NUR als Flaeche und Kante, NIE als Schrift: Prism Indigo ist
|
||||
die einzige Farbe der Palette, die als Text durchfaellt (3,25--4,29:1).
|
||||
Die Schrift bleibt deshalb bewusst Chrome Silver. */
|
||||
.wd-tag--indigo { background: rgb(var(--wd-indigo-rgb) / .16); border-color: rgb(var(--wd-indigo-rgb) / .40); color: rgb(var(--wd-chrome-rgb)); }
|
||||
.wd-tags { display: flex; flex-wrap: wrap; gap: .45rem; }
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
||||
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 75 KiB |
@@ -4,9 +4,16 @@
|
||||
Inhaltsregeln, die hier bewusst eingehalten sind:
|
||||
- Keine erfundenen Kundenbewertungen. Auf dieser Seite steht KEIN
|
||||
einziges Zitat, weil es noch keines gibt, das jemand freigegeben hat.
|
||||
- Keine fremden Projekte ohne Erlaubnis. Beide gezeigten Seiten sind
|
||||
eigene Projekte; die Vorschaubilder sind echte Screenshots der
|
||||
laufenden Seiten vom 22.08.2026, keine Mockups.
|
||||
- Keine fremden Projekte ohne Erlaubnis. Alle fuenf gezeigten Seiten
|
||||
sind eigene Projekte; die Vorschaubilder sind echte Screenshots der
|
||||
laufenden Seiten (Projekt 1-2 vom 22.08.2026, Projekt 3-5 vom
|
||||
24.08.2026), keine Mockups.
|
||||
- Zwei Vorschaubilder sind bewusst NICHT unbearbeitet:
|
||||
Die Buchhaltung zeigt nur die Anmeldung, weil dahinter echte
|
||||
Geschaeftszahlen liegen. Beim Command Center sind die Gesichter
|
||||
unkenntlich gemacht -- die Seite gehoert dem Team, und niemand hat
|
||||
zugestimmt, sein Bild im Portfolio zu zeigen. Beides steht als
|
||||
Hinweis auch fuer die Besucher auf der Seite, nicht nur hier.
|
||||
- Jede Referenz im gleichen Format: Ausgangslage, Lösung, Ergebnis.
|
||||
===================================================================== */
|
||||
window.I18N_WD = {
|
||||
@@ -20,18 +27,18 @@ window.I18N_WD = {
|
||||
|
||||
pf_eyebrow: { de: "Portfolio", "de-CH": "Portfolio", en: "Portfolio", fr: "Portfolio", pt: "Portfólio" },
|
||||
pf_h1: {
|
||||
de: "Zwei Projekte, die man anfassen kann",
|
||||
"de-CH": "Zwei Projekt, wo mer aalange cha",
|
||||
en: "Two projects you can actually visit",
|
||||
fr: "Deux projets que l'on peut réellement visiter",
|
||||
pt: "Dois projetos que se podem mesmo visitar",
|
||||
de: "Fünf Projekte, die man anfassen kann",
|
||||
"de-CH": "Föif Projekt, wo mer aalange cha",
|
||||
en: "Five projects you can actually visit",
|
||||
fr: "Cinq projets que l'on peut réellement visiter",
|
||||
pt: "Cinco projetos que se podem mesmo visitar",
|
||||
},
|
||||
pf_lead: {
|
||||
de: "Beide Seiten laufen wirklich, unter ihrer eigenen Adresse, auf einem eigenen Server. Die Vorschaubilder sind echte Aufnahmen der laufenden Seiten — keine gezeichneten Entwürfe.",
|
||||
"de-CH": "Beidi Site laufed würklich, under ihrer eigene Adress, uf eme eigene Server. D Vorschaubilder sind ächti Ufnahme vo de laufende Site — kei zeichneti Entwürf.",
|
||||
en: "Both sites are genuinely running, at their own address, on their own server. The preview images are real captures of the live sites — not drawn mockups.",
|
||||
fr: "Les deux sites fonctionnent réellement, à leur propre adresse, sur un serveur dédié. Les aperçus sont de vraies captures des sites en ligne — pas des maquettes dessinées.",
|
||||
pt: "Ambos os sites estão mesmo no ar, no seu próprio endereço, em servidor próprio. As pré-visualizações são capturas reais dos sites ativos — não são maquetes desenhadas.",
|
||||
de: "Jede dieser Seiten läuft wirklich, unter ihrer eigenen Adresse. Die Vorschaubilder sind echte Aufnahmen der laufenden Seiten — keine gezeichneten Entwürfe. Wo eine Aufnahme bearbeitet ist, steht der Grund beim Projekt dabei.",
|
||||
"de-CH": "Jedi vo dene Site lauft würklich, under ihrer eigene Adress. D Vorschaubilder sind ächti Ufnahme vo de laufende Site — kei zeichneti Entwürf. Wo en Ufnahm bearbeitet isch, staht de Grund bim Projekt debii.",
|
||||
en: "Every one of these sites is genuinely running, at its own address. The preview images are real captures of the live sites — not drawn mockups. Where a capture has been edited, the reason is given with the project.",
|
||||
fr: "Chacun de ces sites fonctionne réellement, à sa propre adresse. Les aperçus sont de vraies captures des sites en ligne — pas des maquettes dessinées. Lorsqu'une capture a été retouchée, la raison est indiquée avec le projet.",
|
||||
pt: "Cada um destes sites está mesmo no ar, no seu próprio endereço. As pré-visualizações são capturas reais dos sites ativos — não são maquetes desenhadas. Sempre que uma captura foi editada, o motivo é indicado junto ao projeto.",
|
||||
},
|
||||
|
||||
/* Gemeinsame Beschriftungen — gleiches Format für jede Referenz */
|
||||
@@ -147,12 +154,165 @@ window.I18N_WD = {
|
||||
pt: "Não há uma única citação de cliente nesta página. Não por falta de clientes satisfeitos, mas porque ainda ninguém escreveu e aprovou um depoimento — e avaliações inventadas não aparecem aqui por princípio. Assim que existirem reais, estarão aqui. Até lá, prefiro um espaço vazio a um inventado.",
|
||||
},
|
||||
|
||||
|
||||
/* ---------------- Projekt 3: Dogi's ZockerAnstalt ---------------- */
|
||||
p3_h2: { de: "Dogi's ZockerAnstalt", "de-CH": "Dogi's ZockerAnstalt", en: "Dogi's ZockerAnstalt", fr: "Dogi's ZockerAnstalt", pt: "Dogi's ZockerAnstalt" },
|
||||
p3_alt: {
|
||||
de: "Startseite von zockeranstalt.dogfather-universe.com",
|
||||
"de-CH": "Startsite vo zockeranstalt.dogfather-universe.com",
|
||||
en: "Home page of zockeranstalt.dogfather-universe.com",
|
||||
fr: "Page d'accueil de zockeranstalt.dogfather-universe.com",
|
||||
pt: "Página inicial de zockeranstalt.dogfather-universe.com",
|
||||
},
|
||||
p3_art: { de: "Spieleseite", "de-CH": "Spilsite", en: "Games site", fr: "Site de jeux", pt: "Site de jogos" },
|
||||
p3_ausgang: {
|
||||
de: "Die Runde wollte zusammen spielen, ohne sich bei einem fremden Anbieter anzumelden.",
|
||||
"de-CH": "D Rundi hät wölle zäme spile, ohni sich bi eme frönde Aabieter aazmelde.",
|
||||
en: "The group wanted to play together without signing up with an outside provider.",
|
||||
fr: "Le groupe voulait jouer ensemble sans s'inscrire chez un prestataire externe.",
|
||||
pt: "O grupo queria jogar em conjunto sem se registar num fornecedor externo.",
|
||||
},
|
||||
p3_loesung: {
|
||||
de: "Eine eigene Spielhalle mit acht selbst gebauten Spielen, mehrere davon gemeinsam in Echtzeit spielbar.",
|
||||
"de-CH": "En eigeni Spilhalle mit acht sälber boute Spil, mehreri devo zäme i Echtziit spilbar.",
|
||||
en: "An arcade of its own with eight hand-built games, several of them playable together in real time.",
|
||||
fr: "Une salle de jeux maison avec huit jeux créés sur mesure, dont plusieurs jouables ensemble en temps réel.",
|
||||
pt: "Um salão de jogos próprio com oito jogos feitos de raiz, vários deles jogáveis em conjunto em tempo real.",
|
||||
},
|
||||
p3_ergebnis: {
|
||||
de: "Läuft unter eigener Adresse auf dem eigenen Server.",
|
||||
"de-CH": "Lauft under eigener Adress uf em eigene Server.",
|
||||
en: "Running at its own address on its own server.",
|
||||
fr: "En ligne à sa propre adresse, sur un serveur dédié.",
|
||||
pt: "No ar no seu próprio endereço, em servidor próprio.",
|
||||
},
|
||||
p3_f1: { de: "Acht Spiele, jedes einzeln gebaut", "de-CH": "Acht Spil, jedes einzeln boue", en: "Eight games, each built individually", fr: "Huit jeux, chacun développé séparément", pt: "Oito jogos, cada um construído de raiz" },
|
||||
p3_f2: { de: "Mehrspieler in Echtzeit, mit Live-Ranglisten", "de-CH": "Mehrspiler i Echtziit, mit Live-Rangliste", en: "Real-time multiplayer with live rankings", fr: "Multijoueur en temps réel, avec classements en direct", pt: "Multijogador em tempo real, com classificações ao vivo" },
|
||||
p3_f3: { de: "Konten und Bestenlisten", "de-CH": "Konte und Bestelischte", en: "Accounts and high-score tables", fr: "Comptes et tableaux des meilleurs scores", pt: "Contas e tabelas de melhores pontuações" },
|
||||
p3_f4: { de: "Ein Regelwerk zu jedem Spiel", "de-CH": "Es Regelwärch zu jedem Spil", en: "A rulebook for every game", fr: "Un règlement pour chaque jeu", pt: "Um regulamento para cada jogo" },
|
||||
p3_f5: { de: "Eigene Fotos hochladbar, für ein Suchbild daraus", "de-CH": "Eigeni Fotti chasch ufelade, für es Suechbild drus", en: "Upload your own photos to turn them into a spot-the-difference picture", fr: "Possibilité d'importer ses propres photos pour en faire un jeu des différences", pt: "Pode carregar fotos próprias para criar um jogo das diferenças" },
|
||||
p3_stil: {
|
||||
de: "Spielhallen-Optik in Neonfarben — kräftig, aber ohne Flackern und Blinken.",
|
||||
"de-CH": "Spilhalle-Optik i Neonfarbe — kräftig, aber ohni Flackere und Blinke.",
|
||||
en: "Arcade look in neon colours — bold, but with no flicker or blinking.",
|
||||
fr: "Esthétique arcade aux couleurs néon — franche, mais sans scintillement ni clignotement.",
|
||||
pt: "Visual de arcada em cores néon — marcante, mas sem tremeluzir nem piscar.",
|
||||
},
|
||||
|
||||
/* ---------------- Projekt 4: Buchhaltung & Steuern ---------------- */
|
||||
p4_h2: { de: "Buchhaltung & Steuern", "de-CH": "Buechhaltig & Stüüre", en: "Bookkeeping & Tax", fr: "Comptabilité & impôts", pt: "Contabilidade & Impostos" },
|
||||
p4_alt: {
|
||||
de: "Anmeldeseite von buchhaltung.vans-diy-bastelbedarf.com",
|
||||
"de-CH": "Aameldesite vo buchhaltung.vans-diy-bastelbedarf.com",
|
||||
en: "Sign-in page of buchhaltung.vans-diy-bastelbedarf.com",
|
||||
fr: "Page de connexion de buchhaltung.vans-diy-bastelbedarf.com",
|
||||
pt: "Página de início de sessão de buchhaltung.vans-diy-bastelbedarf.com",
|
||||
},
|
||||
p4_art: { de: "Fachanwendung", "de-CH": "Fachaawändig", en: "Specialist application", fr: "Application métier", pt: "Aplicação especializada" },
|
||||
p4_ausgang: {
|
||||
de: "Die Zahlen des Shops lagen in Tabellen und Zetteln verstreut, und vor dem Steuertermin musste alles von Hand zusammengesucht werden.",
|
||||
"de-CH": "D Zahle vom Shop sind i Tabelle und uf Zettel verstreut gsi, und vor em Stüürtermin hät mer alles vo Hand zämesueche müesse.",
|
||||
en: "The shop's figures were scattered across spreadsheets and slips of paper, and everything had to be gathered by hand before the tax deadline.",
|
||||
fr: "Les chiffres de la boutique étaient éparpillés entre tableurs et bouts de papier, et il fallait tout rassembler à la main avant l'échéance fiscale.",
|
||||
pt: "Os números da loja estavam dispersos por folhas de cálculo e papéis, e antes do prazo fiscal era tudo reunido à mão.",
|
||||
},
|
||||
p4_loesung: {
|
||||
de: "Eine eigene Buchhaltung mit doppelter Buchführung, die Verkäufe automatisch aus dem Shop übernimmt.",
|
||||
"de-CH": "En eigeni Buechhaltig mit doppleter Buechfüehrig, wo d Verchäuf automatisch us em Shop übernimmt.",
|
||||
en: "A bookkeeping system of its own using double-entry accounting, which pulls sales in from the shop automatically.",
|
||||
fr: "Une comptabilité maison en partie double, qui récupère automatiquement les ventes depuis la boutique.",
|
||||
pt: "Uma contabilidade própria por partidas dobradas, que importa as vendas da loja automaticamente.",
|
||||
},
|
||||
p4_ergebnis: {
|
||||
de: "Im laufenden Betrieb, mit einer schriftlichen Verfahrensdokumentation an den GoBD entlang.",
|
||||
"de-CH": "Im laufende Betrib, mit ere schriftliche Verfahrensdokumentation em GoBD naa.",
|
||||
en: "In daily use, with written process documentation following the German GoBD rules.",
|
||||
fr: "En service quotidien, avec une documentation écrite des procédures suivant les règles allemandes GoBD.",
|
||||
pt: "Em uso diário, com documentação escrita dos procedimentos segundo as regras alemãs GoBD.",
|
||||
},
|
||||
p4_f1: { de: "Verkäufe kommen alle fünf Minuten selbst aus dem Shop", "de-CH": "Verchäuf chömed alli föif Minute vo sälber us em Shop", en: "Sales arrive from the shop by themselves every five minutes", fr: "Les ventes remontent d'elles-mêmes de la boutique toutes les cinq minutes", pt: "As vendas chegam sozinhas da loja a cada cinco minutos" },
|
||||
p4_f2: { de: "Rechnungen mit fortlaufender Nummer, Korrektur nur per Stornorechnung", "de-CH": "Rächnige mit fortloufender Nummere, Korrektur nur per Stornorächnig", en: "Invoices with consecutive numbering; corrections only via a cancellation invoice", fr: "Factures à numérotation continue ; correction uniquement par facture d'annulation", pt: "Faturas com numeração sequencial; correção apenas por fatura de anulação" },
|
||||
p4_f3: { de: "Kontoauszüge einlesen (CAMT.053, MT940 oder CSV)", "de-CH": "Chontouszüg iiläse (CAMT.053, MT940 oder CSV)", en: "Import bank statements (CAMT.053, MT940 or CSV)", fr: "Import des relevés bancaires (CAMT.053, MT940 ou CSV)", pt: "Importação de extratos bancários (CAMT.053, MT940 ou CSV)" },
|
||||
p4_f4: { de: "Eigene Konten je Kunde und Lieferant", "de-CH": "Eigeni Chonte pro Chund und Lieferant", en: "Separate accounts for each customer and supplier", fr: "Comptes distincts pour chaque client et fournisseur", pt: "Contas próprias para cada cliente e fornecedor" },
|
||||
p4_f5: { de: "Auswertungen entstehen alle aus demselben Buchungsbestand", "de-CH": "D Uuswertige entstöhnd alli us em glyche Buechigsbestand", en: "Every report is generated from one and the same set of entries", fr: "Toutes les analyses proviennent d'un seul et même ensemble d'écritures", pt: "Todas as análises resultam do mesmo conjunto de lançamentos" },
|
||||
p4_stil: {
|
||||
de: "Ruhig und zurückhaltend — bei Zahlen soll nichts vom Lesen ablenken.",
|
||||
"de-CH": "Ruehig und zrugghaltend — bi Zahle söll nüt vom Läse ablänke.",
|
||||
en: "Calm and restrained — with figures, nothing should distract from reading.",
|
||||
fr: "Sobre et discret — face à des chiffres, rien ne doit détourner de la lecture.",
|
||||
pt: "Calmo e discreto — com números, nada deve distrair da leitura.",
|
||||
},
|
||||
p4_kein_link: {
|
||||
de: "Ohne öffentlichen Zugang: Dahinter liegen echte Geschäftszahlen.",
|
||||
"de-CH": "Ohni öffentliche Zuegang: Dehinter ligged ächti Gschäftszahle.",
|
||||
en: "No public access: real business figures lie behind it.",
|
||||
fr: "Pas d'accès public : de vrais chiffres d'entreprise se trouvent derrière.",
|
||||
pt: "Sem acesso público: por trás estão números reais da empresa.",
|
||||
},
|
||||
|
||||
/* ---------------- Projekt 5: Command Center ---------------- */
|
||||
p5_h2: { de: "Command Center", "de-CH": "Command Center", en: "Command Center", fr: "Command Center", pt: "Command Center" },
|
||||
p5_alt: {
|
||||
de: "Startseite von analyse.dogfather-universe.com, die Gesichter sind zum Schutz der Beteiligten unkenntlich gemacht",
|
||||
"de-CH": "Startsite vo analyse.dogfather-universe.com, d Gsichter sind zum Schutz vo de Beteiligte unkenntlich gmacht",
|
||||
en: "Home page of analyse.dogfather-universe.com; faces have been obscured to protect the people shown",
|
||||
fr: "Page d'accueil de analyse.dogfather-universe.com ; les visages sont floutés pour protéger les personnes concernées",
|
||||
pt: "Página inicial de analyse.dogfather-universe.com; os rostos foram desfocados para proteger as pessoas visadas",
|
||||
},
|
||||
p5_art: { de: "Auswertung", "de-CH": "Uuswertig", en: "Analytics", fr: "Analyse", pt: "Análise" },
|
||||
p5_ausgang: {
|
||||
de: "Die Zahlen zu Videos und Reichweite standen an verschiedenen Stellen, und niemand im Team sah dasselbe Bild.",
|
||||
"de-CH": "D Zahle zu Video und Reichwiiti sind a verschidene Ort gstande, und im Team hät niemert s glyche Bild gseh.",
|
||||
en: "The figures on videos and reach sat in different places, and nobody on the team saw the same picture.",
|
||||
fr: "Les chiffres sur les vidéos et la portée se trouvaient à divers endroits, et personne dans l'équipe ne voyait la même chose.",
|
||||
pt: "Os números sobre vídeos e alcance estavam em sítios diferentes, e ninguém na equipa via o mesmo quadro.",
|
||||
},
|
||||
p5_loesung: {
|
||||
de: "Eine gemeinsame Auswertung, zu der jede und jeder im Team einen eigenen Zugang hat.",
|
||||
"de-CH": "En gmeinsami Uuswertig, wo jedi und jede im Team en eigene Zuegang hät.",
|
||||
en: "One shared analytics view, with an individual login for everyone on the team.",
|
||||
fr: "Une analyse commune, avec un accès personnel pour chaque membre de l'équipe.",
|
||||
pt: "Uma análise partilhada, com acesso próprio para cada pessoa da equipa.",
|
||||
},
|
||||
p5_ergebnis: {
|
||||
de: "Im täglichen Einsatz, alle arbeiten mit demselben Stand.",
|
||||
"de-CH": "Im täglichen Iisatz, alli schaffed mit em glyche Stand.",
|
||||
en: "In daily use — everyone works from the same set of numbers.",
|
||||
fr: "Utilisé au quotidien : tout le monde travaille sur les mêmes données.",
|
||||
pt: "Em uso diário: todos trabalham com os mesmos dados.",
|
||||
},
|
||||
p5_f1: { de: "Videos, Zahlen, Monate und Notizen an einer Stelle", "de-CH": "Video, Zahle, Mönet und Notize a einem Ort", en: "Videos, figures, months and notes in one place", fr: "Vidéos, chiffres, mois et notes au même endroit", pt: "Vídeos, números, meses e notas num só sítio" },
|
||||
p5_f2: { de: "Eigener Zugang je Person, mit unterschiedlichen Rechten", "de-CH": "En eigene Zuegang pro Person, mit underschidliche Rächt", en: "A separate login per person, with different permissions", fr: "Un accès distinct par personne, avec des droits différents", pt: "Um acesso próprio por pessoa, com permissões diferentes" },
|
||||
p5_f3: { de: "Zugangscodes selbst änderbar, nichts fest eingebaut", "de-CH": "Zuegangscode chasch sälber ändere, nüt fest iiboue", en: "Access codes can be changed by you; nothing is hard-wired", fr: "Les codes d'accès sont modifiables par vous ; rien n'est figé dans le code", pt: "Os códigos de acesso podem ser alterados por si; nada está fixo no código" },
|
||||
p5_f4: { de: "Als App installierbar", "de-CH": "Als App installierbar", en: "Installable as an app", fr: "Installable comme une application", pt: "Instalável como aplicação" },
|
||||
p5_stil: {
|
||||
de: "Dunkel und aufgeräumt, mit einer ruhigen Leitfarbe je Bereich.",
|
||||
"de-CH": "Dunkel und ufgruumt, mit ere ruehige Leitfarb pro Bereich.",
|
||||
en: "Dark and tidy, with one calm accent colour per area.",
|
||||
fr: "Sombre et ordonné, avec une couleur dominante sobre par section.",
|
||||
pt: "Escuro e arrumado, com uma cor dominante discreta por área.",
|
||||
},
|
||||
p5_hinweis_h3: {
|
||||
de: "Zum Vorschaubild",
|
||||
"de-CH": "Zum Vorschaubild",
|
||||
en: "About the preview image",
|
||||
fr: "À propos de l'aperçu",
|
||||
pt: "Sobre a pré-visualização",
|
||||
},
|
||||
p5_hinweis: {
|
||||
de: "Die Gesichter auf der Aufnahme sind unkenntlich gemacht. Die Seite gehört dem Team, nicht mir — und niemand hat zugestimmt, sein Bild hier zu zeigen.",
|
||||
"de-CH": "D Gsichter uf de Ufnahm sind unkenntlich gmacht. D Site ghört em Team, nöd mir — und niemert hät zuegstimmt, sis Bild do z zeige.",
|
||||
en: "The faces in the capture have been obscured. The site belongs to the team, not to me — and nobody agreed to have their picture shown here.",
|
||||
fr: "Les visages de la capture ont été floutés. Le site appartient à l'équipe, pas à moi — et personne n'a consenti à ce que son image figure ici.",
|
||||
pt: "Os rostos da captura foram desfocados. O site pertence à equipa, não a mim — e ninguém consentiu que a sua imagem fosse mostrada aqui.",
|
||||
},
|
||||
|
||||
pf_cta_h2: {
|
||||
de: "Soll deins das dritte sein?",
|
||||
"de-CH": "Söll dis s dritte si?",
|
||||
en: "Shall yours be the third?",
|
||||
fr: "Le tien sera-t-il le troisième ?",
|
||||
pt: "O teu vai ser o terceiro?",
|
||||
de: "Soll deins das sechste sein?",
|
||||
"de-CH": "Söll dis s sächste si?",
|
||||
en: "Shall yours be the sixth?",
|
||||
fr: "Le tien sera-t-il le sixième ?",
|
||||
pt: "O teu vai ser o sexto?",
|
||||
},
|
||||
pf_cta_p: {
|
||||
de: "Ob dein Projekt hier später auftaucht, entscheidest ausschließlich du — die Aufnahme ins Portfolio ist von der Veröffentlichung deiner Seite getrennt und braucht deine ausdrückliche Erlaubnis.",
|
||||
|
||||
|
Before Width: | Height: | Size: 304 KiB After Width: | Height: | Size: 302 KiB |
|
Before Width: | Height: | Size: 960 KiB After Width: | Height: | Size: 967 KiB |
|
After Width: | Height: | Size: 4.3 MiB |
|
After Width: | Height: | Size: 578 KiB |
@@ -0,0 +1,210 @@
|
||||
/* =====================================================================
|
||||
Prueft die Portfolio-Seite nach den drei neuen Eintraegen.
|
||||
|
||||
Der wichtigste Test hier ist NICHT "sieht huebsch aus", sondern:
|
||||
Hat jeder data-i18n-Schluessel im HTML auch eine Uebersetzung? Ein
|
||||
fehlender Schluessel faellt im Deutschen nicht auf -- der Text steht
|
||||
ja als Rueckfalltext im HTML -- und bricht erst auf, wenn jemand die
|
||||
Sprache wechselt. Genau deshalb wird hier in ALLEN fuenf Sprachen
|
||||
geprueft, nicht nur in einer.
|
||||
===================================================================== */
|
||||
import { chromium } from "playwright";
|
||||
import http from "http";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const TYP = {
|
||||
".html": "text/html", ".css": "text/css", ".js": "text/javascript",
|
||||
".png": "image/png", ".svg": "image/svg+xml", ".webp": "image/webp",
|
||||
".json": "application/json", ".ico": "image/x-icon", ".jpg": "image/jpeg",
|
||||
};
|
||||
|
||||
const fehlt = [];
|
||||
const srv = http.createServer((q, r) => {
|
||||
const u = decodeURIComponent(q.url.split("?")[0]);
|
||||
const p = path.join(process.cwd(), u);
|
||||
if (fs.existsSync(p) && !p.endsWith("/")) {
|
||||
r.writeHead(200, { "content-type": TYP[path.extname(p)] || "text/plain" });
|
||||
return r.end(fs.readFileSync(p));
|
||||
}
|
||||
fehlt.push(u);
|
||||
r.writeHead(404);
|
||||
r.end("");
|
||||
});
|
||||
|
||||
let ok = 0, fehl = 0;
|
||||
const pruefe = (name, gut, info) => {
|
||||
if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); }
|
||||
else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); }
|
||||
};
|
||||
|
||||
/* Kontrast nach WCAG 2.2. Halbtransparente Flaechen muessen ueber
|
||||
ihrem Untergrund verrechnet werden, sonst misst man Unsinn. */
|
||||
function leuchtkraft([r, g, b]) {
|
||||
const f = (v) => {
|
||||
v /= 255;
|
||||
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
||||
};
|
||||
return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b);
|
||||
}
|
||||
function verhaeltnis(a, b) {
|
||||
const l1 = leuchtkraft(a), l2 = leuchtkraft(b);
|
||||
return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
|
||||
}
|
||||
|
||||
srv.listen(4403, async () => {
|
||||
const browser = await chromium.launch();
|
||||
const seite = await browser.newPage({ viewport: { width: 1440, height: 950 } });
|
||||
const skriptfehler = [];
|
||||
seite.on("pageerror", (e) => skriptfehler.push(String(e).slice(0, 120)));
|
||||
|
||||
await seite.goto("http://127.0.0.1:4403/webdesign/portfolio.html");
|
||||
await seite.waitForTimeout(1500);
|
||||
|
||||
/* Die Vorschaubilder tragen loading="lazy" -- der Browser laedt sie
|
||||
erst, wenn sie in Sichtweite kommen. Ohne dieses Vorbeischauen misst
|
||||
der Test "Bild nicht geladen" und meint damit nur, dass er selbst
|
||||
nie hingesehen hat. */
|
||||
await seite.evaluate(async () => {
|
||||
for (const el of document.querySelectorAll(".pfo-bild")) {
|
||||
el.scrollIntoView();
|
||||
await new Promise((r) => setTimeout(r, 250));
|
||||
}
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
await seite.waitForTimeout(1200);
|
||||
|
||||
console.log("1. SIND ALLE FUENF PROJEKTE DA?");
|
||||
const projekte = await seite.evaluate(() =>
|
||||
[...document.querySelectorAll(".pfo-projekt")].map((a) => ({
|
||||
titel: a.querySelector("h2")?.textContent.trim(),
|
||||
bild: a.querySelector("img")?.getAttribute("src")?.replace(/.*\//, ""),
|
||||
geladen: a.querySelector("img")?.naturalWidth > 0,
|
||||
link: a.querySelector("a[href^='http']")?.href || null,
|
||||
})));
|
||||
pruefe("fuenf Projekte auf der Seite", projekte.length === 5, String(projekte.length));
|
||||
for (const p of projekte) {
|
||||
pruefe(` ${p.titel}: Vorschaubild geladen`, p.geladen, p.bild);
|
||||
}
|
||||
|
||||
/* Die Buchhaltung bekommt bewusst KEINEN Live-Knopf: Dahinter liegt
|
||||
nur eine Anmeldemaske, ein Knopf dorthin waere ein leeres
|
||||
Versprechen. Das wird hier festgehalten, damit es niemand spaeter
|
||||
"der Vollstaendigkeit halber" wieder einbaut. */
|
||||
const buch = projekte.find((p) => /Buchhaltung/i.test(p.titel || ""));
|
||||
pruefe("die Buchhaltung hat absichtlich keinen Live-Knopf", buch && !buch.link,
|
||||
buch ? String(buch.link) : "nicht gefunden");
|
||||
|
||||
console.log("\n2. UEBERSETZUNGEN — IN ALLEN FUENF SPRACHEN");
|
||||
/* Geprueft werden die SEITENEIGENEN Schluessel (pf_, p1_-p5_,
|
||||
seiten_titel). Navigations- und Markentexte liegen bewusst nicht
|
||||
hier, sondern in wd-core.js -- sie gelten fuer alle Seiten und
|
||||
werden von server/pruefe-webdesign-i18n.mjs abgedeckt. Sie hier
|
||||
mitzuzaehlen hat nur Fehlalarm erzeugt.
|
||||
|
||||
Und geprueft wird das ERGEBNIS, nicht das Woerterbuch: Die Seite
|
||||
wird wirklich auf jede der fuenf Sprachen umgeschaltet, danach darf
|
||||
kein seiteneigener Text leer sein. */
|
||||
const luecken = [];
|
||||
for (const sprache of ["de", "de-CH", "en", "fr", "pt"]) {
|
||||
const kaputt = await seite.evaluate((spr) => {
|
||||
window.WD.setSprache(spr);
|
||||
const raus = [];
|
||||
const eigen = /^(pf_|p[1-5]_|seiten_titel)/;
|
||||
for (const el of document.querySelectorAll("[data-i18n]")) {
|
||||
const k = el.getAttribute("data-i18n");
|
||||
if (eigen.test(k) && !el.textContent.trim()) raus.push(k + " (leer)");
|
||||
}
|
||||
for (const el of document.querySelectorAll("[data-i18n-attr]")) {
|
||||
const k = el.getAttribute("data-i18n-attr").split(":")[1];
|
||||
const wert = el.getAttribute("alt");
|
||||
if (eigen.test(k) && !(wert && wert.trim())) raus.push(k + " (alt leer)");
|
||||
}
|
||||
return raus;
|
||||
}, sprache);
|
||||
kaputt.forEach((k) => luecken.push(sprache + ": " + k));
|
||||
}
|
||||
pruefe("in jeder Sprache ist jeder seiteneigene Text gefuellt",
|
||||
luecken.length === 0, luecken.slice(0, 4).join(", ") || "alle fuenf Sprachen sauber");
|
||||
|
||||
/* Gegenprobe: Der Test darf nicht deshalb gruen sein, weil die
|
||||
Umschaltung in Wahrheit gar nichts tut. */
|
||||
const wechsel = await seite.evaluate(() => {
|
||||
const el = document.querySelector('[data-i18n="pf_h1"]');
|
||||
window.WD.setSprache("de");
|
||||
const deutsch = el.textContent.trim();
|
||||
window.WD.setSprache("en");
|
||||
const englisch = el.textContent.trim();
|
||||
window.WD.setSprache("de");
|
||||
return { deutsch, englisch };
|
||||
});
|
||||
pruefe("die Umschaltung wirkt wirklich",
|
||||
wechsel.deutsch !== wechsel.englisch, wechsel.englisch);
|
||||
await seite.waitForTimeout(300);
|
||||
|
||||
console.log("\n3. DIE SEITE SPRICHT NICHT MEHR VON ZWEI PROJEKTEN");
|
||||
const zahlwort = await seite.evaluate(() => {
|
||||
const t = window.I18N_WD;
|
||||
const treffer = [];
|
||||
for (const [k, v] of Object.entries(t)) {
|
||||
for (const [s, txt] of Object.entries(v)) {
|
||||
if (/\b(Zwei|Two|Deux|Dois|Beide|Both|Ambos)\b/i.test(txt)) treffer.push(k + "/" + s);
|
||||
}
|
||||
}
|
||||
return treffer;
|
||||
});
|
||||
pruefe("keine Stelle behauptet noch 'zwei' oder 'beide'", zahlwort.length === 0,
|
||||
zahlwort.join(", ") || "sauber");
|
||||
|
||||
console.log("\n4. LESBARKEIT DER NEUEN SCHILDER");
|
||||
const schilder = await seite.evaluate(() => {
|
||||
const holen = (sel) => {
|
||||
const el = document.querySelector(sel);
|
||||
if (!el) return null;
|
||||
const s = getComputedStyle(el);
|
||||
/* Untergrund suchen: das erste Elternteil mit deckender Farbe. */
|
||||
let p = el.parentElement, unter = null;
|
||||
while (p && !unter) {
|
||||
const bg = getComputedStyle(p).backgroundColor;
|
||||
const m = bg.match(/[\d.]+/g);
|
||||
if (m && (m.length === 3 || parseFloat(m[3]) === 1)) unter = m.slice(0, 3).map(Number);
|
||||
p = p.parentElement;
|
||||
}
|
||||
return {
|
||||
text: el.textContent.trim(),
|
||||
farbe: s.color.match(/[\d.]+/g).slice(0, 3).map(Number),
|
||||
eigen: s.backgroundColor.match(/[\d.]+/g),
|
||||
unter: unter || [3, 8, 16],
|
||||
};
|
||||
};
|
||||
return { aqua: holen(".wd-tag--aqua"), indigo: holen(".wd-tag--indigo") };
|
||||
});
|
||||
|
||||
for (const [name, d] of Object.entries(schilder)) {
|
||||
if (!d) { pruefe(`Schild ${name} gefunden`, false); continue; }
|
||||
/* Eigene halbtransparente Flaeche ueber den Untergrund rechnen. */
|
||||
const a = d.eigen.length === 4 ? parseFloat(d.eigen[3]) : 1;
|
||||
const flaeche = d.unter.map((u, i) => Math.round(Number(d.eigen[i]) * a + u * (1 - a)));
|
||||
const v = verhaeltnis(d.farbe, flaeche);
|
||||
pruefe(`Schild "${d.text}" ist gut lesbar (AA = 4,5:1)`, v >= 4.5, v.toFixed(2) + ":1");
|
||||
}
|
||||
|
||||
console.log("\n5. KEINE FEHLER, KEINE FEHLENDEN DATEIEN");
|
||||
pruefe("kein Skriptfehler", skriptfehler.length === 0, skriptfehler[0] || "");
|
||||
const verpasst = fehlt.filter((f) => /\.(jpg|webp|png|css|js|svg)$/.test(f));
|
||||
pruefe("keine fehlende Datei", verpasst.length === 0, verpasst.join(", ") || "keine");
|
||||
|
||||
await seite.screenshot({ path: "pruef-portfolio-computer.png", fullPage: true });
|
||||
const handy = await browser.newPage({ viewport: { width: 390, height: 844 } });
|
||||
await handy.goto("http://127.0.0.1:4403/webdesign/portfolio.html");
|
||||
await handy.waitForTimeout(1500);
|
||||
const ueberlauf = await handy.evaluate(() =>
|
||||
document.documentElement.scrollWidth - document.documentElement.clientWidth);
|
||||
pruefe("auf dem Handy laeuft nichts seitlich ueber", ueberlauf <= 1, ueberlauf + "px");
|
||||
await handy.screenshot({ path: "pruef-portfolio-handy.png", fullPage: true });
|
||||
|
||||
console.log(`\n===== ${ok} bestanden, ${fehl} fehlgeschlagen =====`);
|
||||
await browser.close();
|
||||
srv.close();
|
||||
process.exit(fehl ? 1 : 0);
|
||||
});
|
||||
@@ -9,7 +9,7 @@
|
||||
der Inhalt sonst installiert unter Uhr und Akkuanzeige. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<title>Portfolio — Dogfather Webdesign</title>
|
||||
<meta name="description" content="Echte Projekte statt erfundener Beispiele: Dogfather Universe und Van's DIY & Bastelbedarf — mit Ausgangslage, Lösung und Ergebnis." />
|
||||
<meta name="description" content="Echte Projekte statt erfundener Beispiele: Website, Onlineshop, Spieleseite, Buchhaltung und Auswertung — jeweils mit Ausgangslage, Lösung und Ergebnis." />
|
||||
<meta name="robots" content="noindex, nofollow, noarchive" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/favicon.png" />
|
||||
<meta name="theme-color" content="#05070b" />
|
||||
@@ -65,12 +65,12 @@
|
||||
<section class="wd-abschnitt wd-abschnitt--eng" style="padding-top:clamp(2.4rem,6vw,4rem)">
|
||||
<div class="wd-container">
|
||||
<span class="wd-eyebrow" data-i18n="pf_eyebrow">Portfolio</span>
|
||||
<h1 data-i18n="pf_h1">Zwei Projekte, die man anfassen kann</h1>
|
||||
<p class="wd-lead" style="max-width:72ch" data-i18n="pf_lead">Beide Seiten laufen wirklich, unter ihrer eigenen Adresse, auf einem eigenen Server. Die Vorschaubilder sind echte Aufnahmen der laufenden Seiten — keine gezeichneten Entwürfe.</p>
|
||||
<h1 data-i18n="pf_h1">Fünf Projekte, die man anfassen kann</h1>
|
||||
<p class="wd-lead" style="max-width:72ch" data-i18n="pf_lead">Jede dieser Seiten läuft wirklich, unter ihrer eigenen Adresse. Die Vorschaubilder sind echte Aufnahmen der laufenden Seiten — keine gezeichneten Entwürfe. Wo eine Aufnahme bearbeitet ist, steht der Grund beim Projekt dabei.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Blickfang: der Monolith. Steht zwischen Einleitung und den zwei
|
||||
<!-- Blickfang: der Monolith. Steht zwischen Einleitung und den
|
||||
echten Projekten -- ein ruhiger Moment, bevor die Arbeit kommt.
|
||||
aria-hidden, weil rein atmosphaerisch: Die Aussage der Seite
|
||||
steckt in den Projekten darunter, nicht in diesem Bild. -->
|
||||
@@ -184,6 +184,161 @@
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- ---------------- Projekt 3 ---------------- -->
|
||||
<article class="wd-karte wd-karte--aqua pfo-projekt wd-auf">
|
||||
<img class="pfo-bild" src="/assets/img/wd-projekt-zockeranstalt.jpg"
|
||||
alt="Startseite von zockeranstalt.dogfather-universe.com" width="1200" height="750" loading="lazy" decoding="async"
|
||||
data-i18n-attr="alt:p3_alt" />
|
||||
<div class="pfo-inhalt">
|
||||
<div class="pfo-kopf">
|
||||
<h2 data-i18n="p3_h2">Dogi's ZockerAnstalt</h2>
|
||||
<span class="wd-tag wd-tag--aqua" data-i18n="p3_art">Spieleseite</span>
|
||||
</div>
|
||||
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_ausgang">Ausgangslage</h3>
|
||||
<p data-i18n="p3_ausgang">Die Runde wollte zusammen spielen, ohne sich bei einem fremden Anbieter anzumelden.</p>
|
||||
</div>
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_loesung">Umgesetzte Lösung</h3>
|
||||
<p data-i18n="p3_loesung">Eine eigene Spielhalle mit acht selbst gebauten Spielen, mehrere davon gemeinsam in Echtzeit spielbar.</p>
|
||||
</div>
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_ergebnis">Ergebnis</h3>
|
||||
<p data-i18n="p3_ergebnis">Läuft unter eigener Adresse auf dem eigenen Server.</p>
|
||||
</div>
|
||||
|
||||
<details class="pfo-mehr">
|
||||
<summary data-i18n="pf_details">Details ansehen</summary>
|
||||
<div class="pfo-mehr-inhalt">
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_funktionen">Wichtigste Funktionen</h3>
|
||||
<ul class="wd-liste">
|
||||
<li data-i18n="p3_f1">Acht Spiele, jedes einzeln gebaut</li>
|
||||
<li data-i18n="p3_f2">Mehrspieler in Echtzeit, mit Live-Ranglisten</li>
|
||||
<li data-i18n="p3_f3">Konten und Bestenlisten</li>
|
||||
<li data-i18n="p3_f4">Ein Regelwerk zu jedem Spiel</li>
|
||||
<li data-i18n="p3_f5">Eigene Fotos hochladbar, für ein Suchbild daraus</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_stil">Stilrichtung</h3>
|
||||
<p data-i18n="p3_stil">Spielhallen-Optik in Neonfarben — kräftig, aber ohne Flackern und Blinken.</p>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="wd-btn-reihe" style="margin-top:1rem">
|
||||
<a class="wd-btn wd-btn--zweit wd-btn--klein" href="https://zockeranstalt.dogfather-universe.com/" target="_blank" rel="noopener" data-i18n="pf_ansehen">Live ansehen</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- ---------------- Projekt 4 ---------------- -->
|
||||
<article class="wd-karte wd-karte--vip pfo-projekt wd-auf">
|
||||
<img class="pfo-bild" src="/assets/img/wd-projekt-buchhaltung.jpg"
|
||||
alt="Anmeldeseite von buchhaltung.vans-diy-bastelbedarf.com" width="1200" height="750" loading="lazy" decoding="async"
|
||||
data-i18n-attr="alt:p4_alt" />
|
||||
<div class="pfo-inhalt">
|
||||
<div class="pfo-kopf">
|
||||
<h2 data-i18n="p4_h2">Buchhaltung & Steuern</h2>
|
||||
<span class="wd-tag wd-tag--gold" data-i18n="p4_art">Fachanwendung</span>
|
||||
</div>
|
||||
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_ausgang">Ausgangslage</h3>
|
||||
<p data-i18n="p4_ausgang">Die Zahlen des Shops lagen in Tabellen und Zetteln verstreut, und vor dem Steuertermin musste alles von Hand zusammengesucht werden.</p>
|
||||
</div>
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_loesung">Umgesetzte Lösung</h3>
|
||||
<p data-i18n="p4_loesung">Eine eigene Buchhaltung mit doppelter Buchführung, die Verkäufe automatisch aus dem Shop übernimmt.</p>
|
||||
</div>
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_ergebnis">Ergebnis</h3>
|
||||
<p data-i18n="p4_ergebnis">Im laufenden Betrieb, mit einer schriftlichen Verfahrensdokumentation an den GoBD entlang.</p>
|
||||
</div>
|
||||
|
||||
<details class="pfo-mehr">
|
||||
<summary data-i18n="pf_details">Details ansehen</summary>
|
||||
<div class="pfo-mehr-inhalt">
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_funktionen">Wichtigste Funktionen</h3>
|
||||
<ul class="wd-liste">
|
||||
<li data-i18n="p4_f1">Verkäufe kommen alle fünf Minuten selbst aus dem Shop</li>
|
||||
<li data-i18n="p4_f2">Rechnungen mit fortlaufender Nummer, Korrektur nur per Stornorechnung</li>
|
||||
<li data-i18n="p4_f3">Kontoauszüge einlesen (CAMT.053, MT940 oder CSV)</li>
|
||||
<li data-i18n="p4_f4">Eigene Konten je Kunde und Lieferant</li>
|
||||
<li data-i18n="p4_f5">Auswertungen entstehen alle aus demselben Buchungsbestand</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_stil">Stilrichtung</h3>
|
||||
<p data-i18n="p4_stil">Ruhig und zurückhaltend — bei Zahlen soll nichts vom Lesen ablenken.</p>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Kein "Live ansehen": Dahinter liegt eine Anmeldung, und was
|
||||
dort steht, sind echte Geschäftszahlen. Ein Knopf, der nur
|
||||
zu einer Anmeldemaske führt, wäre ein leeres Versprechen. -->
|
||||
<p class="wd-klein wd-leise" style="margin-top:1rem" data-i18n="p4_kein_link">Ohne öffentlichen Zugang: Dahinter liegen echte Geschäftszahlen.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- ---------------- Projekt 5 ---------------- -->
|
||||
<article class="wd-karte wd-karte--premium pfo-projekt wd-auf">
|
||||
<img class="pfo-bild" src="/assets/img/wd-projekt-analyse.jpg"
|
||||
alt="Startseite von analyse.dogfather-universe.com, die Gesichter sind zum Schutz der Beteiligten unkenntlich gemacht" width="1200" height="750" loading="lazy" decoding="async"
|
||||
data-i18n-attr="alt:p5_alt" />
|
||||
<div class="pfo-inhalt">
|
||||
<div class="pfo-kopf">
|
||||
<h2 data-i18n="p5_h2">Command Center</h2>
|
||||
<span class="wd-tag wd-tag--indigo" data-i18n="p5_art">Auswertung</span>
|
||||
</div>
|
||||
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_ausgang">Ausgangslage</h3>
|
||||
<p data-i18n="p5_ausgang">Die Zahlen zu Videos und Reichweite standen an verschiedenen Stellen, und niemand im Team sah dasselbe Bild.</p>
|
||||
</div>
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_loesung">Umgesetzte Lösung</h3>
|
||||
<p data-i18n="p5_loesung">Eine gemeinsame Auswertung, zu der jede und jeder im Team einen eigenen Zugang hat.</p>
|
||||
</div>
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_ergebnis">Ergebnis</h3>
|
||||
<p data-i18n="p5_ergebnis">Im täglichen Einsatz, alle arbeiten mit demselben Stand.</p>
|
||||
</div>
|
||||
|
||||
<details class="pfo-mehr">
|
||||
<summary data-i18n="pf_details">Details ansehen</summary>
|
||||
<div class="pfo-mehr-inhalt">
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_funktionen">Wichtigste Funktionen</h3>
|
||||
<ul class="wd-liste">
|
||||
<li data-i18n="p5_f1">Videos, Zahlen, Monate und Notizen an einer Stelle</li>
|
||||
<li data-i18n="p5_f2">Eigener Zugang je Person, mit unterschiedlichen Rechten</li>
|
||||
<li data-i18n="p5_f3">Zugangscodes selbst änderbar, nichts fest eingebaut</li>
|
||||
<li data-i18n="p5_f4">Als App installierbar</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="pf_stil">Stilrichtung</h3>
|
||||
<p data-i18n="p5_stil">Dunkel und aufgeräumt, mit einer ruhigen Leitfarbe je Bereich.</p>
|
||||
</div>
|
||||
<!-- Warum das Vorschaubild bearbeitet ist: siehe alt-Text. -->
|
||||
<div class="pfo-block">
|
||||
<h3 data-i18n="p5_hinweis_h3">Zum Vorschaubild</h3>
|
||||
<p data-i18n="p5_hinweis">Die Gesichter auf der Aufnahme sind unkenntlich gemacht. Die Seite gehört dem Team, nicht mir — und niemand hat zugestimmt, sein Bild hier zu zeigen.</p>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="wd-btn-reihe" style="margin-top:1rem">
|
||||
<a class="wd-btn wd-btn--zweit wd-btn--klein" href="https://analyse.dogfather-universe.com/" target="_blank" rel="noopener" data-i18n="pf_ansehen">Live ansehen</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Ehrliche Leerstelle statt erfundener Kundenstimmen. -->
|
||||
<div class="wd-notiz wd-auf">
|
||||
<p><strong data-i18n="pf_regeln_titel">Was hier bewusst fehlt</strong><br />
|
||||
@@ -195,7 +350,7 @@
|
||||
|
||||
<section class="wd-abschnitt wd-abschnitt--gehoben">
|
||||
<div class="wd-container wd-container--schmal" style="text-align:center">
|
||||
<h2 data-i18n="pf_cta_h2">Soll deins das dritte sein?</h2>
|
||||
<h2 data-i18n="pf_cta_h2">Soll deins das sechste sein?</h2>
|
||||
<p class="wd-lead" data-i18n="pf_cta_p">Ob dein Projekt hier später auftaucht, entscheidest ausschließlich du — die Aufnahme ins Portfolio ist von der Veröffentlichung deiner Seite getrennt und braucht deine ausdrückliche Erlaubnis.</p>
|
||||
<div class="wd-btn-reihe" style="justify-content:center">
|
||||
<a class="wd-btn wd-btn--haupt" href="/webdesign/anfrage.html" data-i18n="nav_anfrage">Projekt anfragen</a>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
Start alle alten Zwischenspeicher weg. Muss bei jeder Änderung an den
|
||||
Dateien unten hochgezählt werden, sonst hängen Nutzer auf einem alten
|
||||
Stand fest. */
|
||||
const CACHE_NAME = "dogfather-webdesign-v33";
|
||||
const CACHE_NAME = "dogfather-webdesign-v34";
|
||||
|
||||
/* Bausteine, die die Oberfläche zum Anzeigen braucht. Bewusst KEINE
|
||||
HTML-Datei in dieser Liste. */
|
||||
|
||||