Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions public/fest/fest_quest2_shop_list.csv

Large diffs are not rendered by default.

Binary file added public/images/fest/mapMark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/fest/quest-3-1-banner.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/fest/quest-3-2-banner.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/fest/quest-3-3-banner.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/fest/quest-3-4-banner.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/fest/quest-3-5-banner.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
110 changes: 110 additions & 0 deletions src/components/fest/quest2_csv_reader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import fs from 'fs';
import path from 'path';

// Read CSV file with UTF-8 encoding
const csvPath = path.join(process.cwd(), 'public', 'fest', 'fest_quest2_shop_list_4.csv');
const csvContent = fs.readFileSync(csvPath, 'utf-8');

// Parse CSV content with proper handling of quoted fields
function parseCSV(content: string) {
// Handle different line endings (Windows \r\n, Unix \n, Mac \r)
const lines = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');

if (lines.length === 0) return [];

// Parse headers
const headers = parseCSVLine(lines[0]);
const shops = [];

for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim();
if (!line) continue;

const values = parseCSVLine(line);

if (values.length >= 3) { // At least have number, name, zone
const shop: any = {};
headers.forEach((header, index) => {
const value = values[index] || '';
shop[header.trim()] = value.trim();
if(header === 'image'){
shop[header] = convertGoogleDriveUrl(value.trim())
}
});

// Only add shops that have a name and zone
if (shop.name && shop.zone) {
shops.push(shop);
}
}
}
return shops;
}

// Helper function to parse a single CSV line handling quoted fields
function parseCSVLine(line: string): string[] {
const result = [];
let current = '';
let inQuotes = false;
let i = 0;

while (i < line.length) {
const char = line[i];

if (char === '"') {
if (inQuotes && line[i + 1] === '"') {
// Handle escaped quotes inside quoted field
current += '"';
i += 2;
continue;
} else {
// Toggle quote state
inQuotes = !inQuotes;
}
} else if (char === ',' && !inQuotes) {
// End of field
result.push(current);
current = '';
} else {
current += char;
}
i++;
}

// Push the last field
result.push(current);

return result;
}


// Helper function to convert Google Drive share links to direct image URLs
function convertGoogleDriveUrl(url: string): string {
console.log('Original URL:', url);

if (!url) return '';

// Match Google Drive URLs and extract file ID
const driveRegex = /(?:drive\.google\.com\/(?:file\/d\/|open\?id=))([a-zA-Z0-9_-]+)/;
const match = url.match(driveRegex);

if (match && match[1]) {
// Try multiple formats for better compatibility
const fileId = match[1];

// Format 1: Standard thumbnail (works best for public images)
const directUrl = `https://drive.google.com/thumbnail?id=${fileId}&sz=w400`;

console.log('Converted URL:', directUrl);
return directUrl;
}

console.log('Not a Google Drive URL, returning original:', url);
return url; // Return original URL if it's not a Google Drive URL
}

const allShops = parseCSV(csvContent);
export const zoneAShops = allShops.filter(shop => shop.zone === 'A');
export const zoneBShops = allShops.filter(shop => shop.zone === 'B');
export const zoneCShops = allShops.filter(shop => shop.zone === 'C');
export const zoneDShops = allShops.filter(shop => shop.zone === 'D');
145 changes: 103 additions & 42 deletions src/pages/fest/quest-1.astro

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions src/pages/fest/quest-2-A.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
import Layout from "@/layouts/fest/FestWithNavbar.astro";
import HorizonlineFest from "@/components/common/HorizonlineFest.astro";
import ButtonFest from "@/components/common/ButtonFest.astro";
import {zoneAShops} from "@/components/fest/quest2_csv_reader"
---

<Layout>
<div class="flex flex-col space-y-2 px-4 py-6">
<div
class="sticky top-0 z-10 flex w-full justify-between px-3 py-2 backdrop-blur-md"
>
<a href="/fest/quest-2" class="mt-2.5 text-2xl font-bold">{"<"}</a>
<div class="flex flex-col gap-1 text-center">
<p class="font-family-orbitron text-5xl">Quest 2 - A</p>
<p class="text-2xl font-extralight">ร้านค้าโซน A</p>
</div>
<a href="/fest/quest-2-B" class="mt-2.5 text-2xl font-bold">{">"}</a>
</div>

<div class="space-y-6 pt-4">
{zoneAShops.map((shop, index) => (
<div class="shop-item">
{index > 0 && <HorizonlineFest variant="blue" />}

<div class="space-y-4">
{/* Shop Name */}
<h2 class="text-2xl font-bold text-center text-pink-500">
{shop.name}
</h2>

{/* Shop Image */}
{shop.image && (
<div class="flex justify-center">
<img
src={shop.image}
alt={shop.name}
class="max-w-full h-48 object-cover rounded-lg shadow-md"
loading="lazy"
onerror="console.log('Image failed to load:', this.src); this.style.display='none';"
onload="console.log('Image loaded successfully:', this.src);"
/>
</div>
)}

<div class="p-4">
<h3 class="font-semibold text-lg text-pink-400 mb-2">เมนูแนะนำ:</h3>
<p
class="text-justify leading-relaxed hyphens-auto text-white"
lang="th"
style="text-align: justify; text-align-last: left; word-spacing: 0.1em;"
>
{shop.manu}
</p>
</div>
<div class="p-4">
<h3 class="font-semibold text-lg text-pink-400 mb-2">รายละเอียด:</h3>
<p
class="text-justify leading-relaxed hyphens-auto text-white"
lang="th"
style="text-align: justify; text-align-last: left; word-spacing: 0.1em;"
>
{shop.description}
</p>
</div>
<div class="p-4">
<h3 class="font-semibold text-lg text-pink-400 mb-2">เวลาเปิด-ปิด:</h3>
<p
class="text-justify leading-relaxed hyphens-auto text-white"
lang="th"
style="text-align: justify; text-align-last: left; word-spacing: 0.1em;"
>
{shop.time}
</p>
</div>


{/* Google Maps Button */}
{shop.maps && (
<div class="flex justify-center pt-2">
<ButtonFest
color="pink"
size="big"
href={shop.maps}
target="_blank"
rel="noopener noreferrer"
>
Google Maps
</ButtonFest>
</div>
)}
</div>
</div>
))}
</div>
</div>
</Layout>
97 changes: 97 additions & 0 deletions src/pages/fest/quest-2-B.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
import Layout from "@/layouts/fest/FestWithNavbar.astro";
import HorizonlineFest from "@/components/common/HorizonlineFest.astro";
import ButtonFest from "@/components/common/ButtonFest.astro";
import {zoneBShops} from "@/components/fest/quest2_csv_reader"
---

<Layout>
<div class="flex flex-col space-y-2 px-4 py-6">
<div
class="sticky top-0 z-10 flex w-full justify-between px-3 py-2 backdrop-blur-md"
>
<a href="/fest/quest-2-A" class="mt-2.5 text-2xl font-bold">{"<"}</a>
<div class="flex flex-col gap-1 text-center">
<p class="font-family-orbitron text-5xl">Quest 2 - B</p>
<p class="text-2xl font-extralight">ร้านค้าโซน B</p>
</div>
<a href="/fest/quest-2-C" class="mt-2.5 text-2xl font-bold">{">"}</a>
</div>

<div class="space-y-6 pt-4">
{zoneBShops.map((shop, index) => (
<div class="shop-item">
{index > 0 && <HorizonlineFest variant="blue" />}

<div class="space-y-4">
{/* Shop Name */}
<h2 class="text-2xl font-bold text-center text-pink-500">
{shop.name}
</h2>

{/* Shop Image */}
{shop.image && (
<div class="flex justify-center">
<img
src={shop.image}
alt={shop.name}
class="max-w-full h-48 object-cover rounded-lg shadow-md"
loading="lazy"
onerror="console.log('Image failed to load:', this.src); this.style.display='none';"
onload="console.log('Image loaded successfully:', this.src);"
/>
</div>
)}

<div class="p-4">
<h3 class="font-semibold text-lg text-pink-400 mb-2">เมนูแนะนำ:</h3>
<p
class="text-justify leading-relaxed hyphens-auto text-white"
lang="th"
style="text-align: justify; text-align-last: left; word-spacing: 0.1em;"
>
{shop.manu}
</p>
</div>
<div class="p-4">
<h3 class="font-semibold text-lg text-pink-400 mb-2">รายละเอียด:</h3>
<p
class="text-justify leading-relaxed hyphens-auto text-white"
lang="th"
style="text-align: justify; text-align-last: left; word-spacing: 0.1em;"
>
{shop.description}
</p>
</div>
<div class="p-4">
<h3 class="font-semibold text-lg text-pink-400 mb-2">เวลาเปิด-ปิด:</h3>
<p
class="text-justify leading-relaxed hyphens-auto text-white"
lang="th"
style="text-align: justify; text-align-last: left; word-spacing: 0.1em;"
>
{shop.time}
</p>
</div>


{/* Google Maps Button */}
{shop.maps && (
<div class="flex justify-center pt-2">
<ButtonFest
color="pink"
size="big"
href={shop.maps}
target="_blank"
rel="noopener noreferrer"
>
Google Maps
</ButtonFest>
</div>
)}
</div>
</div>
))}
</div>
</div>
</Layout>
Loading
Loading