fix: ui component updates and project formatting
Some checks failed
Build & Deploy / 🔍 Prepare (push) Failing after 4s
Build & Deploy / 🧪 QA (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Failing after 4s
Build & Deploy / 🧪 QA (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
This commit is contained in:
110
scripts/fix_map.mjs
Normal file
110
scripts/fix_map.mjs
Normal file
@@ -0,0 +1,110 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
// 1. Rename Kabeltiefbau to Kabelnetzbau
|
||||
const filesToReplace = [];
|
||||
function walkDir(dir) {
|
||||
if (!fs.existsSync(dir)) return;
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stat = fs.statSync(fullPath);
|
||||
if (stat.isDirectory()) {
|
||||
if (['node_modules', '.next', '.git'].includes(file)) continue;
|
||||
walkDir(fullPath);
|
||||
} else {
|
||||
if (fullPath.match(/\.(tsx|ts|mdx|json)$/)) {
|
||||
filesToReplace.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const baseDir = '/Volumes/Alpha SSD/Coding/e-tib.com';
|
||||
walkDir(path.join(baseDir, 'content'));
|
||||
walkDir(path.join(baseDir, 'messages'));
|
||||
walkDir(path.join(baseDir, 'components'));
|
||||
walkDir(path.join(baseDir, 'app'));
|
||||
|
||||
for (const file of filesToReplace) {
|
||||
let content = fs.readFileSync(file, 'utf-8');
|
||||
let newContent = content
|
||||
.replace(/Kabeltiefbau/g, 'Kabelnetzbau')
|
||||
.replace(/kabeltiefbau/g, 'kabelnetzbau')
|
||||
.replace(/KABELTIEFBAU/g, 'KABELNETZBAU');
|
||||
|
||||
if (content !== newContent) {
|
||||
fs.writeFileSync(file, newContent, 'utf-8');
|
||||
console.log(`Replaced in ${file}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Parse text files and update map-data.ts
|
||||
const txtDir = '/Users/marcmintel/Downloads/etib';
|
||||
const txtFiles = ['Orte 2016 - 2022.txt', 'Orte 2023.txt', 'Orte 2024.txt', 'Orte 2025.txt', 'Orte 2026.txt'];
|
||||
const projectMapping = {}; // plz -> "Typ, PLZ Ort"
|
||||
|
||||
for (const tf of txtFiles) {
|
||||
const content = fs.readFileSync(path.join(txtDir, tf), 'utf-8');
|
||||
const lines = content.split('\n');
|
||||
for (let line of lines) {
|
||||
line = line.trim();
|
||||
if (!line) continue;
|
||||
|
||||
// Extract PLZ
|
||||
const match = line.match(/\b\d{5}\b/);
|
||||
if (match) {
|
||||
const plz = match[0];
|
||||
projectMapping[plz] = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read map-data.ts
|
||||
const mapDataPath = path.join(baseDir, 'lib/map-data.ts');
|
||||
let mapData = fs.readFileSync(mapDataPath, 'utf-8');
|
||||
|
||||
// The objects look like:
|
||||
// {
|
||||
// id: 'göhlsdorf-14797',
|
||||
// name: 'PV-Infrastrukturprojekt, Region Berlin / Brandenburg',
|
||||
// type: 'minor_node',
|
||||
// x: 68.78,
|
||||
// y: 35.98,
|
||||
// description: 'pv',
|
||||
// },
|
||||
|
||||
// We will use regex to find each block and replace name and type
|
||||
let updatedMapData = mapData.replace(/id:\s*'([^']+)',\s*name:\s*'([^']+)',\s*type:\s*'([^']+)',/g, (match, id, name, type) => {
|
||||
// Find PLZ in ID
|
||||
const plzMatch = id.match(/\d{5}/);
|
||||
if (plzMatch && projectMapping[plzMatch[0]]) {
|
||||
const correctName = projectMapping[plzMatch[0]].replace(/'/g, "\\'");
|
||||
console.log(`Updating ${id} -> ${correctName}`);
|
||||
return `id: '${id}',\n name: '${correctName}',\n type: 'project',`;
|
||||
}
|
||||
// If it's a minor node but no PLZ match, still change to project?
|
||||
if (type === 'minor_node') {
|
||||
return `id: '${id}',\n name: '${name}',\n type: 'project',`;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
// Fix Petershagen coordinates
|
||||
updatedMapData = updatedMapData.replace(
|
||||
/id:\s*'petershagen-15326'[\s\S]*?x:\s*[\d.]+,\s*y:\s*[\d.]+/,
|
||||
(match) => {
|
||||
return match.replace(/x:\s*[\d.]+,\s*y:\s*[\d.]+/, 'x: 82.5, y: 33.5');
|
||||
}
|
||||
);
|
||||
|
||||
// Also fix the defaultLocations petershagen if it exists
|
||||
updatedMapData = updatedMapData.replace(
|
||||
/id:\s*'petershagen'[\s\S]*?x:\s*36\.26,\s*y:\s*35\.79/,
|
||||
(match) => {
|
||||
return match.replace(/x:\s*36\.26,\s*y:\s*35\.79/, 'x: 82.5, y: 33.5');
|
||||
}
|
||||
);
|
||||
|
||||
fs.writeFileSync(mapDataPath, updatedMapData, 'utf-8');
|
||||
console.log('map-data.ts updated!');
|
||||
41
scripts/geocode-missing.js
Normal file
41
scripts/geocode-missing.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const missing = [
|
||||
{ id: 'lautzenhausen-55483', name: 'PV-Anlage, 55483 Lautzenhausen', desc: 'pv', zip: '55483' },
|
||||
{ id: 'schleiz-07907', name: 'Breitbandausbau, 07907 Schleiz', desc: 'fiber', zip: '07907' },
|
||||
{ id: 'grosshartau-01909', name: 'PV-Anlage, 01909 Großhartau', desc: 'pv', zip: '01909' },
|
||||
{ id: 'halsbrücke-09600', name: 'Breitbandausbau, 09600 Halsbrücke', desc: 'fiber', zip: '09600' },
|
||||
{ id: 'seinsheim-97342', name: 'PV-Anlage, 97342 Seinsheim', desc: 'pv', zip: '97342' },
|
||||
{ id: 'schenkenberg-17291', name: 'Kabelschutzrohrtrasse, 17291 Schenkenberg-Polen', desc: 'power', zip: '17291' },
|
||||
{ id: 'thundorf-97711', name: 'PV-Anlage, 97711 Thundorf OT Theinfeld', desc: 'pv', zip: '97711' },
|
||||
{ id: 'tarnow-18249', name: 'Windpark, 18249 Tarnow-Prüzen', desc: 'wind', zip: '18249' },
|
||||
{ id: 'ennigerloh-59320', name: 'PV-Anlage, 59320 Ennigerloh-Oelde', desc: 'pv', zip: '59320' }
|
||||
];
|
||||
|
||||
async function geocode() {
|
||||
for (const item of missing) {
|
||||
try {
|
||||
const res = await fetch(`https://nominatim.openstreetmap.org/search?postalcode=${item.zip}&country=Germany&format=json`);
|
||||
const data = await res.json();
|
||||
if (data && data.length > 0) {
|
||||
const lat = parseFloat(data[0].lat);
|
||||
const lon = parseFloat(data[0].lon);
|
||||
const x = (lon - 4.64) * 8.395;
|
||||
const y = (55.215 - lat) * 12.589;
|
||||
|
||||
console.log(` {`);
|
||||
console.log(` id: '${item.id}',`);
|
||||
console.log(` name: '${item.name}',`);
|
||||
console.log(` type: 'minor_node',`);
|
||||
console.log(` x: ${x.toFixed(2)},`);
|
||||
console.log(` y: ${y.toFixed(2)},`);
|
||||
console.log(` description: '${item.desc}',`);
|
||||
console.log(` },`);
|
||||
} else {
|
||||
console.log(`// Could not geocode ${item.zip}`);
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(`// Error geocoding ${item.zip}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
geocode();
|
||||
64
scripts/geocode-new.ts
Normal file
64
scripts/geocode-new.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import fs from 'fs';
|
||||
|
||||
const newLocations = [
|
||||
{ raw: 'NS-Verkabelung, Klein Gastrose, 03172 Schenkendöbern', zip: '03172', type: 'power', city: 'Schenkendöbern' },
|
||||
{ raw: 'MS-Verkabelung, Auras-Cottbus, 03051 Cottbus', zip: '03051', type: 'power', city: 'Cottbus' },
|
||||
{ raw: 'MS-Verkabelung, 15848 Rietz-Neuendorf', zip: '15848', type: 'power', city: 'Rietz-Neuendorf' },
|
||||
{ raw: 'NS-Verkabelung, 15910 Schlepzig', zip: '15910', type: 'power', city: 'Schlepzig' },
|
||||
{ raw: 'MS-Verkabelung, 15306 Falkenhagen Mark', zip: '15306', type: 'power', city: 'Falkenhagen Mark' },
|
||||
{ raw: 'MS-Verkabelung, 15898 Neuzelle', zip: '15898', type: 'power', city: 'Neuzelle' },
|
||||
{ raw: 'MS-Verkabelung, 15890 Eisenhüttenstadt OT Diehlo', zip: '15890', type: 'power', city: 'Eisenhüttenstadt' },
|
||||
{ raw: 'MS-Verkabelung, 15848 Karras', zip: '15848', type: 'power', city: 'Karras' },
|
||||
{ raw: 'MS-Verkabelung, Gewerbegebiet, 14669 Ketzin', zip: '14669', type: 'power', city: 'Ketzin' },
|
||||
{ raw: 'MS-Verkabelung, 15868 Lieberose', zip: '15868', type: 'power', city: 'Lieberose' },
|
||||
{ raw: 'MS-Verkabelung, 15344 Strausberg', zip: '15344', type: 'power', city: 'Strausberg' },
|
||||
{ raw: 'MS-Verkabelung, 15366 Neuenhagen', zip: '15366', type: 'power', city: 'Neuenhagen' },
|
||||
{ raw: 'MS-Verkabelung, 15868 Jamlitz', zip: '15868', type: 'power', city: 'Jamlitz' },
|
||||
{ raw: 'MS-Verkabelung, 03096 Burg (Spreewald)', zip: '03096', type: 'power', city: 'Burg' },
|
||||
{ raw: 'MS-Verkabelung, 03130 Bohsdorf', zip: '03130', type: 'power', city: 'Bohsdorf' },
|
||||
{ raw: 'Windpark, 29575 Altenmedingen', zip: '29575', type: 'wind', city: 'Altenmedingen' }
|
||||
];
|
||||
|
||||
async function geocode() {
|
||||
let output = '';
|
||||
for (const item of newLocations) {
|
||||
try {
|
||||
const res = await fetch(`https://nominatim.openstreetmap.org/search?postalcode=${item.zip}&country=Germany&format=json`, {
|
||||
headers: {
|
||||
'User-Agent': 'E-TIB Internal Script / info@mintel.com'
|
||||
}
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data && data.length > 0) {
|
||||
// slightly jitter the coordinates to prevent perfect overlap if there are duplicates
|
||||
const jitterLat = (Math.random() - 0.5) * 0.01;
|
||||
const jitterLon = (Math.random() - 0.5) * 0.01;
|
||||
const lat = parseFloat(data[0].lat) + jitterLat;
|
||||
const lon = parseFloat(data[0].lon) + jitterLon;
|
||||
const x = (lon - 4.64) * 8.395;
|
||||
const y = (55.215 - lat) * 12.589;
|
||||
|
||||
const id = item.city.toLowerCase().replace(/[^a-z0-9]/g, '-') + '-' + item.zip;
|
||||
|
||||
output += ` {\n`;
|
||||
output += ` id: '${id}',\n`;
|
||||
output += ` name: '${item.raw}',\n`;
|
||||
output += ` type: 'minor_node',\n`;
|
||||
output += ` x: ${x.toFixed(2)},\n`;
|
||||
output += ` y: ${y.toFixed(2)},\n`;
|
||||
output += ` description: '${item.type}',\n`;
|
||||
output += ` },\n`;
|
||||
} else {
|
||||
console.log(`// Could not geocode ${item.zip}`);
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(`// Error geocoding ${item.zip}`);
|
||||
}
|
||||
// wait 1 second to respect nominatim rate limits
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
}
|
||||
|
||||
console.log(output);
|
||||
}
|
||||
|
||||
geocode();
|
||||
110
scripts/patch_header.js
Normal file
110
scripts/patch_header.js
Normal file
@@ -0,0 +1,110 @@
|
||||
const fs = require('fs');
|
||||
|
||||
let headerCode = fs.readFileSync('components/layout/Header.tsx', 'utf8');
|
||||
|
||||
// Insert MobileMenu state
|
||||
headerCode = headerCode.replace(
|
||||
"const [headerVariant, setHeaderVariant] = React.useState<'capsule' | 'standard'>('capsule');",
|
||||
"const [headerVariant, setHeaderVariant] = React.useState<'capsule' | 'standard'>('capsule');\n const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);\n"
|
||||
);
|
||||
|
||||
// Close menu on pathname change
|
||||
headerCode = headerCode.replace(
|
||||
"// Re-check when pathname changes",
|
||||
"// Re-check when pathname changes\n setIsMobileMenuOpen(false);"
|
||||
);
|
||||
|
||||
// Add Hamburger Button in the Header
|
||||
const hamburgerButton = `
|
||||
{/* Hamburger Menu (Mobile Only) */}
|
||||
<button
|
||||
className="md:hidden ml-2 p-2 text-white hover:text-primary transition-colors focus:outline-none"
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
aria-label="Toggle Menu"
|
||||
>
|
||||
<div className="w-6 h-5 flex flex-col justify-between relative">
|
||||
<span className={\`w-full h-0.5 bg-current transform transition duration-300 ease-in-out \${isMobileMenuOpen ? 'rotate-45 translate-y-2.5' : ''}\`} />
|
||||
<span className={\`w-full h-0.5 bg-current transition duration-300 ease-in-out \${isMobileMenuOpen ? 'opacity-0' : 'opacity-100'}\`} />
|
||||
<span className={\`w-full h-0.5 bg-current transform transition duration-300 ease-in-out \${isMobileMenuOpen ? '-rotate-45 -translate-y-2' : ''}\`} />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
`;
|
||||
headerCode = headerCode.replace(
|
||||
" </div>\n </div>\n </div>\n </header>",
|
||||
hamburgerButton
|
||||
);
|
||||
|
||||
// Add Mobile Menu Overlay
|
||||
const mobileMenuOverlay = `
|
||||
{/* Mobile Menu Overlay */}
|
||||
<AnimatePresence>
|
||||
{isMobileMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="fixed inset-0 z-[40] bg-[#050B14]/95 backdrop-blur-xl md:hidden pt-24 pb-24 px-6 overflow-y-auto"
|
||||
>
|
||||
<nav className="flex flex-col gap-6 mt-8">
|
||||
{navLinks && navLinks.length > 0 && navLinks.map((link) => {
|
||||
const mappedUrl = link.url.startsWith('/') && !link.url.match(/^\\/(en|de)/)
|
||||
? \`/\${currentLocale}\${link.url}\`
|
||||
: link.url;
|
||||
const isActive = pathname === mappedUrl || (mappedUrl !== \`/\${currentLocale}\` && pathname?.startsWith(\`\${mappedUrl}/\`));
|
||||
|
||||
return (
|
||||
<div key={link.url} className="flex flex-col">
|
||||
<TransitionLink
|
||||
href={mappedUrl}
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className={\`text-2xl font-bold uppercase tracking-widest transition-colors \${isActive ? 'text-primary' : 'text-white'}\`}
|
||||
>
|
||||
{link.label}
|
||||
</TransitionLink>
|
||||
|
||||
{link.children && (
|
||||
<div className="flex flex-col gap-3 mt-3 pl-4 border-l-2 border-white/10">
|
||||
{link.children.map((child, idx) => {
|
||||
if (child.isGroupLabel) {
|
||||
return <div key={idx} className="text-sm font-bold text-neutral-500 uppercase tracking-widest mt-2">{child.label}</div>;
|
||||
}
|
||||
const childUrl = child.url.startsWith('/') && !child.url.match(/^\\/(en|de)/)
|
||||
? \`/\${currentLocale}\${child.url}\`
|
||||
: child.url;
|
||||
const isChildActive = pathname === childUrl;
|
||||
|
||||
return (
|
||||
<TransitionLink
|
||||
key={child.url}
|
||||
href={childUrl}
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className={\`text-lg font-medium transition-colors \${isChildActive ? 'text-primary' : 'text-white/70'}\`}
|
||||
>
|
||||
{child.label}
|
||||
</TransitionLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
}
|
||||
`;
|
||||
headerCode = headerCode.replace(
|
||||
" </header>\n </>\n );\n}",
|
||||
` </header>\n${mobileMenuOverlay}`
|
||||
);
|
||||
|
||||
fs.writeFileSync('components/layout/Header.tsx', headerCode);
|
||||
Reference in New Issue
Block a user