diff --git a/Dockerfile b/Dockerfile index 6503605..db84889 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,6 +27,9 @@ RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/public ./public +# Create data directory for JSON storage and uploads +RUN mkdir -p /data && chown nextjs:nodejs /data + # Set the correct permission for prerender cache RUN mkdir .next RUN chown nextjs:nodejs .next diff --git a/docker-compose.yml b/docker-compose.yml index 56acf14..ce8ea8b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,9 @@ services: - SMTP_USER=${SMTP_USER} - SMTP_PASS=${SMTP_PASS} - SMTP_FROM=${SMTP_FROM} + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + volumes: + - xhivart-data:/data networks: - traefik-public labels: @@ -24,6 +27,9 @@ services: retries: 3 start_period: 15s +volumes: + xhivart-data: + networks: traefik-public: external: true diff --git a/next.config.ts b/next.config.ts index dceb131..a78550f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -12,6 +12,19 @@ const nextConfig: NextConfig = { pathname: "/images/**", }, ], + remotePatterns: [ + { + hostname: "localhost", + }, + ], + }, + async rewrites() { + return [ + { + source: "/uploads/:path*", + destination: "/api/uploads/:path*", + }, + ]; }, async headers() { return [ diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx new file mode 100644 index 0000000..90cac9e --- /dev/null +++ b/src/app/about/page.tsx @@ -0,0 +1,257 @@ +import Image from 'next/image'; +import Link from 'next/link'; + +export const metadata = { + title: 'About Ximena Xaguar | XHIVA ART', + description: 'Multidisciplinary visionary artist working at the intersection of art, ritual and embodied presence.', +}; + +export default function AboutPage() { + return ( + <> + {/* Navigation — simplified for sub-page */} + + +
+ {/* Hero */} +
+
+
+ {/* Portrait */} +
+ Ximena Xaguar +
+ +
+

+ THE ARTIST +

+

+ About Ximena +

+
+

+ Ximena Xaguar is a multidisciplinary visionary artist, healer and cultural + producer based in Zürich, Switzerland, with deep roots in Bolivia. Her + work weaves ancestral memory with contemporary expression across painting, + ceremony, immersive gatherings and community art. +

+

+ With over fifteen years of practice guiding Temazcal ceremonies, crystal + healing sessions and transformative group experiences, Ximena creates spaces + where art becomes a lived experience — a bridge between inner worlds + and shared reality. +

+

+ Her paintings emerge from cycles of transformation, shadow work, intuitive + vision and ancestral cosmovision. Each artwork is a portal — an ally + for contemplation, energetic coherence and spiritual insight. +

+
+
+
+
+ + {/* Extended Biography */} +
+
+
+

+ BIOGRAPHY +

+

+ The Path +

+
+
+ +
+

+ Born in La Paz, Bolivia, Ximena grew up immersed in the rich cultural + tapestry of the Andes — a world where art, ceremony and daily life + are inseparable. This early foundation shaped her understanding of art as + something alive, relational and deeply connected to land and community. +

+

+ She studied Fine Arts and later expanded her practice through years of + apprenticeship in ancestral healing traditions across Bolivia and Peru. + The Temazcal (sweatlodge) became a central pillar of her ceremonial work, + which she has guided for over fifteen years. +

+

+ Moving to Switzerland, Ximena founded Re Evolution Art — a cultural + platform bridging South American ancestral wisdom with European contemporary + art. Through events like Visionary Art Week Zürich, TRIBAL Nights and + PULSAR, she creates spaces where artists, musicians, ritualists and seekers + converge. +

+

+ Her artistic practice spans large-scale canvas painting, murals, live + performance and collaborative installation. Each work draws on symbolic + language, universal cosmovision and the transformative power of colour + and form. +

+

+ Today, Ximena continues to paint, guide ceremonies and produce cultural + events from her base in Zürich, while maintaining deep connections + to her Bolivian roots and the broader network of visionary artists and + healers worldwide. +

+
+
+
+ + {/* Second Portrait */} +
+
+
+
+

+ PRACTICE +

+

+ Art as Ritual +

+
+

+ For Ximena, every painting is a ceremony. The studio becomes a ritual + space — candles, incense, music and intention set the container + for creation. The painting process mirrors the inner journey: death + and rebirth, shadow and light, dissolution and integration. +

+

+ Her works are not decorative objects but living presences — portals + that continue to work on the viewer long after the first encounter. + Collectors and participants consistently describe a felt sense of + connection, activation and deep recognition when engaging with her art. +

+
+
+ Ximena painting +
+
+
+
+ + {/* Art in Motion */} +
+
+
+

+ THE PROCESS +

+

+ Art in Motion +

+
+
+
+ {[ + { src: '/images/about/painting-process.webp', alt: 'Ximena painting', position: 'top' }, + { src: '/images/art/mural-bio-centro.webp', alt: 'Mural Bio Centro Guembe' }, + { src: '/images/about/portrait-2.jpg', alt: 'Ximena painting mural' }, + { src: '/images/about/portrait-3.jpg', alt: 'Ximena in studio', position: 'top' }, + { src: '/images/about/portrait-4.jpg', alt: 'Ximena at ceremony' }, + { src: '/images/about/pachamama.jpg', alt: 'Ritual with smoke' }, + ].map((photo, index) => ( +
+ {photo.alt} +
+ ))} +
+
+
+ + {/* CTA */} +
+
+

Explore Further

+
+
+ + View Gallery + + + Services + + + Get in Touch + +
+
+
+
+ + {/* Footer */} + + + ); +} diff --git a/src/app/admin/events/page.tsx b/src/app/admin/events/page.tsx new file mode 100644 index 0000000..9a3f502 --- /dev/null +++ b/src/app/admin/events/page.tsx @@ -0,0 +1,206 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import type { ArtEvent } from '@/lib/types'; + +export default function AdminEvents() { + const [events, setEvents] = useState([]); + const [editing, setEditing] = useState(null); + const [isNew, setIsNew] = useState(false); + const [loading, setLoading] = useState(true); + + const fetchEvents = async () => { + const res = await fetch('/api/admin/events'); + setEvents(await res.json()); + setLoading(false); + }; + + useEffect(() => { fetchEvents(); }, []); + + const handleSave = async () => { + if (!editing) return; + const method = isNew ? 'POST' : 'PUT'; + const url = isNew ? '/api/admin/events' : `/api/admin/events/${editing.id}`; + await fetch(url, { + method, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(editing), + }); + setEditing(null); + setIsNew(false); + fetchEvents(); + }; + + const handleDelete = async (id: string) => { + if (!confirm('Delete this event?')) return; + await fetch(`/api/admin/events/${id}`, { method: 'DELETE' }); + fetchEvents(); + }; + + const handleUpload = async (file: File): Promise => { + const form = new FormData(); + form.append('file', file); + const res = await fetch('/api/admin/upload', { method: 'POST', body: form }); + const data = await res.json(); + return data.url || ''; + }; + + const newEvent = (): ArtEvent => ({ + id: `event-${Date.now()}`, + title: '', + description: '', + image: '', + date: '', + link: '', + section: 'reevolution', + sortOrder: events.length + 1, + }); + + if (loading) return
Loading...
; + + const reevolutionEvents = events.filter(e => e.section === 'reevolution').sort((a, b) => a.sortOrder - b.sortOrder); + const xhivaEvents = events.filter(e => e.section === 'xhiva').sort((a, b) => a.sortOrder - b.sortOrder); + + return ( +
+
+

Events

+ +
+ + {/* Edit Modal */} + {editing && ( +
+
+

{isNew ? 'New Event' : 'Edit Event'}

+
+
+ + setEditing({...editing, title: e.target.value})} + className="w-full px-3 py-2 border rounded-lg text-sm focus:outline-none focus:border-[#c9a962]" /> +
+
+ +