Skip to content
Open

Dev #47

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
ad59697
feat: pwa
dkcodec Feb 3, 2026
01669f9
feat: pwa
dkcodec Feb 3, 2026
538c451
Merge pull request #38 from dkcodec/pwa
dkcodec Feb 3, 2026
24838b9
feat: PWA + notifictaion
dkcodec Feb 4, 2026
20ebed1
upd: refresh to main page
dkcodec Feb 6, 2026
ffba374
Merge pull request #39 from dkcodec/pwa
dkcodec Feb 6, 2026
bac0f9f
feat: schedule
dkcodec Feb 24, 2026
db9cb25
prettier
dkcodec Feb 24, 2026
080a296
update: new endpoints, new logic
dkcodec Mar 10, 2026
ccb3cc0
pettier: -fix
dkcodec Mar 10, 2026
3706818
Merge pull request #40 from dkcodec/ref/newFlow
dkcodec Mar 10, 2026
18800c2
empty
dkcodec Mar 10, 2026
b0a543c
Merge pull request #41 from dkcodec/ref/newFlow
dkcodec Mar 10, 2026
57fdd20
upd: staff search
dkcodec Mar 14, 2026
fe3042c
Merge branch 'upd/schedule' of https://github.com/dkcodec/app-bagsy i…
dkcodec Mar 14, 2026
94797e6
upd: schedule
dkcodec Mar 30, 2026
75508b6
format
dkcodec Mar 30, 2026
c4cbdee
feat: account page
dkcodec Mar 31, 2026
642b676
fix: popular
dkcodec Mar 31, 2026
f04cb2c
feat: locations, cubscription
dkcodec Mar 31, 2026
e5c6527
upd: schedule
dkcodec Mar 31, 2026
7fffc39
format
dkcodec Mar 31, 2026
05bab86
upd: style
dkcodec Mar 31, 2026
138744b
upd: organization, activation for point
dkcodec Mar 31, 2026
95a5aed
upd: docs
dkcodec Mar 31, 2026
bfb8906
upd: docs
dkcodec Mar 31, 2026
5b2d981
feat: schedule for locations in network
dkcodec Mar 31, 2026
8b2584c
format
dkcodec Mar 31, 2026
d791d1c
upd: service
dkcodec Mar 31, 2026
fff70e5
move: master-service to employees-service
dkcodec Mar 31, 2026
a41e436
udp: staff
dkcodec Apr 1, 2026
490e5a1
udp: service to vault bottom sheet
dkcodec Apr 1, 2026
c1fbd76
udp: close app-sidebar
dkcodec Apr 1, 2026
0b83a7a
add: staff docs
dkcodec Apr 1, 2026
5a645de
format
dkcodec Apr 1, 2026
581d95f
fix: logout cache
dkcodec Apr 1, 2026
8e6b31c
upd: calendar
dkcodec Apr 1, 2026
de893c8
upd: calendar url, rm: refs, useless loaders
dkcodec Apr 1, 2026
151fc51
feat: color to service seelct
dkcodec Apr 1, 2026
6792154
feat: select sidebar location
dkcodec Apr 1, 2026
7502727
upd: memo schedule
dkcodec Apr 1, 2026
5ecc7bc
upd: memo schedule
dkcodec Apr 1, 2026
b5bee87
fix: localization for add event modal
dkcodec Apr 1, 2026
b9fbc36
fix: localization for add event modal
dkcodec Apr 1, 2026
2ae364f
upd: deps
dkcodec Apr 1, 2026
30573d2
feat: unlink service, unlink location
dkcodec Apr 2, 2026
0b2e504
feat: unlink service, unlink location
dkcodec Apr 2, 2026
d5abf52
feat: unlink service, unlink location
dkcodec Apr 2, 2026
336890a
feat: global location select for services
dkcodec Apr 2, 2026
61c6e30
upd: collapsed sidebar
dkcodec Apr 2, 2026
fdec943
upd: sidebar styles
dkcodec Apr 2, 2026
0f32db2
upd: menu hover color
dkcodec Apr 2, 2026
64ab2a9
fix: logout clear persist
dkcodec Apr 2, 2026
6e247ff
upd: logo
dkcodec Apr 2, 2026
3d75155
upd: logo
dkcodec Apr 2, 2026
cb689c7
upd: auth in-memory reset
dkcodec Apr 2, 2026
268966b
udp: current event
dkcodec Apr 2, 2026
94c29ec
Merge pull request #42 from dkcodec/ref/newFlow
dkcodec Apr 2, 2026
650f7a3
fix: schedule
dkcodec Apr 2, 2026
08c060f
Merge pull request #43 from dkcodec/fix/schedule
dkcodec Apr 2, 2026
d16a4b6
fix: schedule
dkcodec Apr 5, 2026
095244b
fix: disabled hours
dkcodec Apr 5, 2026
8e2e99f
fix: schedule
dkcodec Apr 5, 2026
cc255d0
fix: schecdule when removed, saved days
dkcodec Apr 5, 2026
d3b1461
upd: detach only for network
dkcodec Apr 5, 2026
e43c386
feat: showing if unlinked master of service for add-event
dkcodec Apr 5, 2026
9a8f0b2
Merge pull request #44 from dkcodec/fix/schedule
dkcodec Apr 5, 2026
4863dea
fix: service staff map
dkcodec Apr 5, 2026
cac06ce
Merge pull request #45 from dkcodec/fix/schedule
dkcodec Apr 5, 2026
c84408b
feat: staff service view
dkcodec Apr 5, 2026
836816c
Merge pull request #46 from dkcodec/fix/schedule
dkcodec Apr 6, 2026
def8191
fix: schedule for tofay
dkcodec Apr 7, 2026
f876e29
Merge pull request #48 from dkcodec/fix/schedule
dkcodec Apr 7, 2026
e64182d
fix: toast
dkcodec May 16, 2026
aa846f6
Merge pull request #49 from dkcodec/fix/translations
dkcodec May 16, 2026
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
2 changes: 1 addition & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"permissions": {
"allow": ["mcp__acp__Edit"]
"allow": ["mcp__acp__Edit", "Bash(npx next build)"]
}
}
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ yarn-error.log*
# vercel
.vercel

# Serwist (PWA)
/public/sw.js
/public/sw.js.map
/public/swe-worker-*.js
/public/swe-worker-*.js.map
/public/workbox-*.js
/public/workbox-*.js.map
/public/serialized-*.js
/public/serialized-*.js.map

# typescript
*.tsbuildinfo
next-env.d.ts

certificates
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

## PWA

Приложение настроено как PWA (manifest, Serwist, офлайн-страница, иконки).

- **Проверка:** Chrome DevTools → Application → Manifest, Service Workers.
- **Lighthouse:** запустить аудит PWA и Performance по HTTPS (`next build` + `next start` или деплой).
- **Локально с HTTPS:** `next dev --experimental-https` для тестов установки и push (если позже добавите).
2 changes: 1 addition & 1 deletion app/[locale]/(auth)/invite/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default async function InvitePage({
let tokenData;
try {
tokenData = await AuthService.verifyAuthToken(token);
} catch (error) {
} catch {
// Если токен невалиден (401) или произошла ошибка - показываем not-found
notFound();
}
Expand Down
10 changes: 10 additions & 0 deletions app/[locale]/(sidebar)/account/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use client";

import { AccountPage } from "@/src/features/account";

/**
* Страница аккаунта — объединяет профиль, подписку, безопасность, настройки
*/
export default function Page() {
return <AccountPage />;
}
131 changes: 131 additions & 0 deletions app/[locale]/(sidebar)/locations/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"use client";

import { use } from "react";
import { useTranslations } from "next-intl";
import { Card, CardContent, Skeleton } from "@/src/entities";
import { LocationsHeader } from "@/src/features/locations";
import { LocationDetailView } from "@/src/features/locations/components/location-detail-view";
import { useLocation } from "@/src/shared/hooks/use-network-locations";
import { useCurrentUser } from "@/src/shared/hooks/use-users";
import { EUserRole } from "@/src/shared/types/user";

/**
* Скелетон для детального вида локации
*/
function DetailSkeleton() {
return (
<div className="flex flex-col gap-5 p-4 mx-auto w-full">
{/* Back button */}
<Skeleton className="h-8 w-32" />
{/* Header */}
<div className="flex items-start justify-between">
<div className="space-y-2">
<Skeleton className="h-6 w-48" />
<Skeleton className="h-4 w-64" />
</div>
<div className="flex gap-2">
<Skeleton className="h-6 w-16 rounded-full" />
<Skeleton className="h-8 w-8" />
</div>
</div>
{/* Info grid */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<Skeleton className="h-16 rounded-lg" />
<Skeleton className="h-16 rounded-lg" />
<Skeleton className="h-16 rounded-lg" />
</div>
{/* Map */}
<Skeleton className="h-[200px] rounded-lg" />
{/* Working hours */}
<div className="space-y-1">
<Skeleton className="h-5 w-28 mb-2" />
{Array.from({ length: 7 }).map((_, i) => (
<Skeleton key={i} className="h-10 w-full rounded-lg" />
))}
</div>
{/* Stats */}
<div className="grid grid-cols-2 gap-3">
<Skeleton className="h-20 rounded-lg" />
<Skeleton className="h-20 rounded-lg" />
</div>
{/* Booking link */}
<Skeleton className="h-20 rounded-lg" />
</div>
);
}

/**
* Страница детального просмотра локации
* Sub-route: /locations/[id]
*/
export default function LocationDetailPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = use(params);
const t = useTranslations("Locations");
const { data: currentUser, isLoading: userLoading } = useCurrentUser();
const { data: location, isLoading, error } = useLocation(id);

// Загрузка — скелетон
if (isLoading || userLoading) {
return (
<>
<LocationsHeader />
<DetailSkeleton />
</>
);
}

// Проверка доступа (Owner / Manager)
const hasAccess =
currentUser &&
(currentUser.role === EUserRole.OWNER ||
currentUser.role === EUserRole.MANAGER);

if (!hasAccess) {
return (
<>
<LocationsHeader />
<div className="flex flex-col md:p-4">
<Card className="border-none">
<CardContent className="p-6 text-center">
<p className="font-semibold mb-2 text-destructive">
{t("accessDenied")}
</p>
<p className="text-sm text-muted-foreground">
{t("accessDeniedDescription")}
</p>
</CardContent>
</Card>
</div>
</>
);
}

// Ошибка загрузки
if (error || !location) {
return (
<>
<LocationsHeader />
<div className="flex flex-col md:p-4">
<Card className="border-none">
<CardContent className="p-6 text-center">
<p className="text-sm text-muted-foreground">
{t("errorLoading")}
</p>
</CardContent>
</Card>
</div>
</>
);
}

return (
<>
<LocationsHeader />
<LocationDetailView location={location} showBack />
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { PointsHeader, PointsContent } from "@/src/features/points";
import { LocationsHeader, LocationsContent } from "@/src/features/locations";
import { useCurrentUser } from "@/src/shared/hooks/use-users";
import { EUserRole } from "@/src/shared/types/user";
import { useTranslations } from "next-intl";
Expand All @@ -11,9 +11,9 @@ import { Loader } from "lucide-react";
* Страница точек обслуживания
* Доступна только для ролей MANAGER и выше
*/
export default function PointsPage() {
export default function LocationsPage() {
const { data: currentUser, isLoading } = useCurrentUser();
const t = useTranslations("Points");
const t = useTranslations("Locations");

// Проверка загрузки пользователя
if (isLoading) {
Expand All @@ -25,17 +25,16 @@ export default function PointsPage() {
}

// Проверка доступа
// Доступ: Owner и Manager
const hasAccess =
currentUser &&
(currentUser.role === EUserRole.MANAGER ||
currentUser.role === EUserRole.SELF_OWNER ||
currentUser.role === EUserRole.NET_MANAGER ||
currentUser.role === EUserRole.ADMIN);
(currentUser.role === EUserRole.OWNER ||
currentUser.role === EUserRole.MANAGER);

if (!hasAccess) {
return (
<>
<PointsHeader />
<LocationsHeader />
<div className="flex flex-col md:p-4">
<Card className="border-none">
<CardContent className="p-6">
Expand All @@ -56,8 +55,8 @@ export default function PointsPage() {

return (
<>
<PointsHeader />
<PointsContent />
<LocationsHeader />
<LocationsContent />
</>
);
}
24 changes: 0 additions & 24 deletions app/[locale]/(sidebar)/profile/page.tsx

This file was deleted.

6 changes: 6 additions & 0 deletions app/[locale]/(sidebar)/schedule/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { SchedulePageClient } from "@/src/features/schedule";

/** Страница графика: помесячное расписание точки/мастера, пресеты и перерывы. */
export default function SchedulePage() {
return <SchedulePageClient />;
}
16 changes: 0 additions & 16 deletions app/[locale]/(sidebar)/settings/page.tsx

This file was deleted.

21 changes: 21 additions & 0 deletions app/[locale]/offline/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use client";

import { Button } from "@/src/entities";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";

export default function OfflinePage() {
const t = useTranslations("Offline");
const router = useRouter();

return (
<div className="flex flex-col items-center justify-center min-h-screen p-4">
<div className="text-center space-y-4">
<div className="text-6xl">📡</div>
<h1 className="text-3xl font-bold">{t("title")}</h1>
<p className="text-gray-200 max-w-md">{t("description")}</p>
<Button onClick={() => router.push("/")}>{t("retry")}</Button>
</div>
</div>
);
}
17 changes: 17 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default function RootLayout({
return (
<html suppressHydrationWarning>
<head>
{/* Favicon */}
<link
rel="icon"
href="/logo-light.svg"
Expand All @@ -31,6 +32,22 @@ export default function RootLayout({
type="image/svg+xml"
media="(prefers-color-scheme: dark)"
/>
{/* PWA: theme-color для standalone */}
<meta
name="theme-color"
content="#ffffff"
media="(prefers-color-scheme: light)"
/>
<meta
name="theme-color"
content="#0a0a0a"
media="(prefers-color-scheme: dark)"
/>
{/* Apple: установка на главный экран */}
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta name="apple-mobile-web-app-title" content="Bagsy" />
<link rel="apple-touch-icon" href="/icon-192x192.png" sizes="192x192" />
</head>
<body className={nunito.className} suppressHydrationWarning>
<QueryProvider>
Expand Down
39 changes: 39 additions & 0 deletions app/manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { MetadataRoute } from "next";

// Константы PWA (manifest обычно на одном языке)
const PWA_NAME = "Bagsy — управление записями";
const PWA_SHORT_NAME = "Bagsy";
const PWA_DESCRIPTION = "Управление записями и календарь";

export default function manifest(): MetadataRoute.Manifest {
return {
name: PWA_NAME,
short_name: PWA_SHORT_NAME,
description: PWA_DESCRIPTION,
start_url: "/",
scope: "/",
display: "standalone",
background_color: "#ffffff",
theme_color: "#000000",
icons: [
{
src: "/icon-192x192.png",
sizes: "192x192",
type: "image/png",
purpose: "any",
},
{
src: "/icon-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "any",
},
{
src: "/icon-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "maskable",
},
],
};
}
Loading