Easy-to-manage touch screens
무료 오픈소스 터치스크린으로 USB 설정 가이드, 드래그‑드롭 편집기, 로컬 제어를 제공하며 Home Assistant용으로 제작되었습니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Official Tourism & Hospitality Platform for Hargeisa.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A production-ready tourism platform for Hargeisa, Somaliland — built with Next.js 14 (App
Router), TypeScript, Tailwind CSS, Supabase and Framer Motion. Supports English, Arabic (RTL)
and Af-Soomaali, with dark mode, an interactive map, a full admin CMS, and a real content model
for hotels, restaurants, cafes, attractions, events and a blog.README 전문 보기 ↗A production-ready tourism platform for Hargeisa, Somaliland — built with Next.js 14 (App Router), TypeScript, Tailwind CSS, Supabase and Framer Motion. Supports English, Arabic (RTL) and Af-Soomaali, with dark mode, an interactive map, a full admin CMS, and a real content model for hotels, restaurants, cafes, attractions, events and a blog.
Build verification note: this project was developed in a sandboxed environment with no network access, so
npm installwas never actually run here — every file was reviewed by hand for type-correctness, import correctness, brace/paren balance, and Next.js App Router / Server Component rules (see §10 for the specific checks performed). Run a realnpm install && npm run buildbefore deploying — see §8 for a CI workflow that does this automatically on every push.
Public site — homepage (animated hero, trust stats, destinations, hotels, restaurants,
cafes, attractions, events, local experiences, featured businesses, blog, interactive map,
newsletter), Hotels/Restaurants/Cafes/Attractions (list with working ?q= search + detail pages
with gallery, breadcrumbs, map, amenities/menu, reviews, review submission, favorite toggle, "Add
to Trip"), Events (filterable list + detail), Blog (Markdown articles), Travel Guide, Shopping,
Transportation, Explore (neighborhoods), About, Contact (real Supabase-backed form).
Authentication — Supabase email/password login & registration with email confirmation,
signed-in state resolved client-side in the header (avatar, name, sign out) so public pages stay
statically generated (see §7 on this trade-off), ?next= redirect support, auth callback route.
User dashboard (/dashboard, protected by a real server-side auth guard):
favorites tableavatars Storage bucket)Admin CMS (/admin, protected by a real server-side admin-role guard) — complete CRUD for
all six content types, each with a list view (search-free table, edit, delete) and Add/Edit forms
with Supabase Storage image upload:
Database — 20-table Postgres schema (supabase/schema.sql) with Row Level Security on every
table: public read access to published content, admin-only writes, owners can edit their own
listings, users manage their own reviews/favorites/trips/profile. Two Storage buckets
(listing-images admin-only, avatars per-user-folder) with matching RLS policies. A seed
script pushes sample data into a freshly connected project.
i18n — next-intl, instant switching between English / Arabic (RTL) / Af-Soomaali. The
language switcher uses a real Somaliland flag SVG (public/flags/somaliland.svg) — Unicode has
no emoji flag for Somaliland, and this app never substitutes Somalia's flag for it.
SEO — per-page metadata with correct per-locale canonical URLs (see §10), Open Graph/Twitter
cards, JSON-LD (Hotel, TouristDestination, BreadcrumbList), dynamic sitemap.xml built from real
data, robots.txt disallowing /admin, /dashboard, /auth, plus noindex on those sections
via route-segment layouts as a second line of defense.
PWA — manifest, service worker, installable icons.
Performance — see §7 for the public/authenticated client split that makes public pages
eligible for static generation + hourly ISR, and React.cache() deduping repeated Supabase reads
within a single request (e.g. generateMetadata + the page component both reading the same
listing).
All public pages read through lib/data/*.ts, which queries Supabase when configured and
transparently falls back to lib/mock-data.ts only when no project is connected — so the app
runs immediately for preview, but nothing in the actual page code depends on placeholder data.
Every image everywhere is a single coverImage/image string field, either a labeled on-brand
placeholder (lib/placeholder-image.ts, swappable in one line) or a real Supabase Storage URL
uploaded through the admin panel — there's no hardcoded image anywhere outside those two paths.
.nvmrc)npm install
cp .env.example .env.local
# Leave the Supabase values blank for now — the site runs on placeholder
# data until you connect a project (see §4).
npm run dev
Open http://localhost:3000 — you'll be redirected to /en. Try /ar and /so to see Arabic
(RTL) and Af-Soomaali.
supabase/schema.sql — creates all 20 tables, enums, indexes,
triggers (auto-syncing listing ratings from reviews), RLS policies, and the two Storage
buckets (listing-images, avatars) with their policies.Project URL, anon public key, and service_role key from Settings → API
into .env.local:
NEXT_PUBLIC_SUPABASE_URL=...
NEXT_PUBLIC_SUPABASE_ANON_KEY=...
SUPABASE_SERVICE_ROLE_KEY=...
npm run seed./admin:
update profiles set role = 'admin' where id = '<your auth.users id>';
https://yourdomain.com/*/auth/callback as a redirect URL for email confirmations to work in
production.types/database.ts is hand-written to match schema.sql exactly. Once your schema is final,
replace it with generated types:
npx supabase gen types typescript --project-id <your-project-ref> > types/database.ts
app/[locale]/
layout.tsx # fonts, <html lang/dir>, providers — deliberately does NOT read
# cookies/auth, so it doesn't force every page dynamic (see §7)
page.tsx # homepage
hotels|restaurants|cafes|attractions/
page.tsx # listing + ?q= search, ISR revalidate=3600
[slug]/page.tsx # detail: breadcrumbs, reviews, review form, favorite, add-to-trip
events|blog/ # list + [slug] detail
admin/
layout.tsx # noindex for the whole section
page.tsx # overview + stats
users/page.tsx # role management
hotels|restaurants|cafes|attractions|events|articles/
page.tsx # list (AdminListTable) + delete
new/page.tsx # create form + image upload
[id]/edit/page.tsx # edit form + image upload
dashboard/
layout.tsx # noindex
page.tsx # fetches favorites/trips/reviews/profile, renders DashboardTabs
auth/login|register|callback/
components/
layout/ # header (client-side auth via useHeaderUser), footer, language switcher, user menu
home/ # homepage sections
admin/ # AdminListTable, form-shared (Field/TagInput), one *-form.tsx per content type
dashboard/ # DashboardTabs, SavedTripsPanel, ReviewsPanel, ProfilePanel
shared/ # gallery, reviews, review form, listing card (favorite toggle), search box,
# breadcrumbs, image uploader, delete button, add-to-trip button, flag icon,
# coordinates-input (paste-a-Google-Maps-link-or-type-lat/lng, no map/API key)
lib/
supabase/ # server.ts (cookie-aware, for auth-dependent reads/writes), public.ts
# (no cookies — for public content, enables ISR), client.ts (browser), storage.ts
# (uploads), guards.ts (requireUser/requireAdmin), middleware.ts, is-configured.ts
data/ # one file per content type — queries Supabase (public client for public content,
# server client for user-specific data), falls back to mock-data.ts otherwise.
# getXBySlug functions are wrapped in React.cache() to dedupe per-request calls.
actions/ # server actions: admin CRUD, content (newsletter/contact/reviews), favorites,
# trips, profile, users
i18n/ # next-intl config — includes the Somaliland flag asset reference
placeholder-image.ts / mock-data.ts
supabase/schema.sql # full schema + RLS + Storage policies
scripts/seed.ts
.github/workflows/ci.yml # type-check, lint, build on every push/PR
Add the same key to messages/en.json, ar.json, so.json, then useTranslations() (works in
both Server and Client Components with next-intl v3) or getTranslations().
Two Supabase clients exist on purpose:
lib/supabase/public.ts (createPublicClient) — no cookies, used by every lib/data/*.ts
function that reads public content (hotels, restaurants, cafes, attractions, events, articles,
destinations, map points). Because it never calls cookies(), pages using only this client are
eligible for static generation and the export const revalidate = 3600 (hourly ISR) set on
every public listing/detail page.lib/supabase/server.ts (createClient) — cookie-aware, used only where the signed-in
user's identity actually matters: favorites, saved trips, review history, profile, admin
writes, auth guards. These routes are correctly dynamic (personalized), and that's fine.The layout resolves auth server-side. app/[locale]/layout.tsx already sets export const dynamic = "force-dynamic" (next-intl's request-based APIs require it in the installed version),
so the whole route tree renders dynamically on every request regardless of what the layout does.
Given that, getHeaderUser() (lib/supabase/guards.ts) reads the cookie-aware client once per
request and passes the result into <SiteHeader initialUser={...}> — the header is correct on
first paint, with no "signed out" flash and no client/server mismatch. A client-side subscription
in components/layout/use-header-user.ts keeps it live after sign-in/sign-out without a full page
reload. Public listing/detail pages still get their own ISR via export const revalidate = 3600
on the page itself — only the shared layout wrapping them is forced dynamic.
getXBySlug functions in lib/data/*.ts are wrapped in React's cache(), so a detail page's
generateMetadata and the page component itself (which both call the same function) only hit
Supabase once per request instead of twice.
.env.local variables in Project Settings → Environment Variables.vercel.json sets sensible caching headers for images and the service worker..github/workflows/ci.yml runs type-check, lint, and build on every push/PR — it deliberately
builds without Supabase env vars set, so it exercises the mock-data fallback path rather than
hitting a real database from CI. Add real secrets under repo Settings → Secrets if you want CI to
build against your actual project instead.
NEXT_PUBLIC_SITE_URL to your real domain (used by metadataBase, sitemap.ts, robots.ts)public/icons/ and app/icon.png/apple-icon.pngImageUploader supports multiple buckets/
folders but each form only wires up a single image field — extend for full photo galleries.public/flags/somaliland.svg for an official
file if you have one; every other reference points at this one path.Verified by static analysis in this sandbox (no network access):
.ts/.tsx file is brace/paren-balanced (scripted check across all ~160 files)generateMetadata returns a correct per-locale alternates.canonicallib/data/*.ts file reading public content still imports the cookie-aware clientHeaderUser after moving it out of site-header.tsx)Not verified (requires an actual environment): a real npm install && npm run build, ESLint
passing cleanly beyond config validity, and runtime behavior of the Supabase auth cookie flow. Run
.github/workflows/ci.yml's steps locally first if you want a final check before deploying.
Next.js 14 · React 18 · TypeScript · Tailwind CSS · Supabase (Postgres + Auth + Storage) · next-intl · Framer Motion · lucide-react · next-themes. No map SDK of any kind — every location action is a plain link out to Google Maps (lib/utils/google-maps.ts).