feat(super-admin): перенести справочник Стран в системную консоль
Country — глобальный справочник (Entity, не TenantEntity), магазины- клиенты выбирают страны из готового списка но не управляют ими. Управление переносится в SuperAdmin консоль. Изменения: - API: POST/PUT /api/catalog/countries теперь Authorize(Roles=SuperAdmin) (раньше был SuperAdmin,Admin). DELETE и так был SuperAdmin. - GET остаётся [Authorize] без роли — нужен tenant'у для селектов в формах создания орги/контрагентов/товаров. - Tenant AppLayout: убран блок «Справочники» с пунктом «Страны». Иконка Globe больше не импортируется в tenant-меню. - Tenant роут /catalog/countries удалён из App.tsx. - В OrganizationSettingsPage ссылка «откройте справочник Страны» заменена на текст «обратитесь к администратору платформы». - SuperAdminLayout: новый блок «Справочники» с пунктом «Страны» (/super-admin/countries). Иконка Globe. - Роут /super-admin/countries использует существующий CountriesPage — компонент unchanged, страница теперь рендерится в SuperAdminLayout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
61cb97a1b7
commit
8d72e9da2d
|
|
@ -57,7 +57,7 @@ public async Task<ActionResult<CountryDto>> Get(Guid id, CancellationToken ct)
|
||||||
return c is null ? NotFound() : Project(c);
|
return c is null ? NotFound() : Project(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost, Authorize(Roles = "SuperAdmin,Admin")]
|
[HttpPost, Authorize(Roles = "SuperAdmin")]
|
||||||
public async Task<ActionResult<CountryDto>> Create([FromBody] CountryInput input, CancellationToken ct)
|
public async Task<ActionResult<CountryDto>> Create([FromBody] CountryInput input, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var e = new Country
|
var e = new Country
|
||||||
|
|
@ -73,7 +73,7 @@ public async Task<ActionResult<CountryDto>> Create([FromBody] CountryInput input
|
||||||
return CreatedAtAction(nameof(Get), new { id = e.Id }, Project(e));
|
return CreatedAtAction(nameof(Get), new { id = e.Id }, Project(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{id:guid}"), Authorize(Roles = "SuperAdmin,Admin")]
|
[HttpPut("{id:guid}"), Authorize(Roles = "SuperAdmin")]
|
||||||
public async Task<IActionResult> Update(Guid id, [FromBody] CountryInput input, CancellationToken ct)
|
public async Task<IActionResult> Update(Guid id, [FromBody] CountryInput input, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var e = await _db.Countries.FirstOrDefaultAsync(x => x.Id == id, ct);
|
var e = await _db.Countries.FirstOrDefaultAsync(x => x.Id == id, ct);
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ export default function App() {
|
||||||
<Route path="organizations" element={<SuperAdminOrganizationsPage />} />
|
<Route path="organizations" element={<SuperAdminOrganizationsPage />} />
|
||||||
<Route path="organizations/new" element={<SuperAdminOrgCreatePage />} />
|
<Route path="organizations/new" element={<SuperAdminOrgCreatePage />} />
|
||||||
<Route path="audit-log" element={<SuperAdminAuditLogPage />} />
|
<Route path="audit-log" element={<SuperAdminAuditLogPage />} />
|
||||||
|
<Route path="countries" element={<CountriesPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
{/* Tenant-роуты — обычный AppLayout, но с TenantRouteGuard:
|
{/* Tenant-роуты — обычный AppLayout, но с TenantRouteGuard:
|
||||||
|
|
@ -73,7 +74,6 @@ export default function App() {
|
||||||
<Route path="/catalog/counterparties" element={<CounterpartiesPage />} />
|
<Route path="/catalog/counterparties" element={<CounterpartiesPage />} />
|
||||||
<Route path="/catalog/stores" element={<StoresPage />} />
|
<Route path="/catalog/stores" element={<StoresPage />} />
|
||||||
<Route path="/catalog/retail-points" element={<RetailPointsPage />} />
|
<Route path="/catalog/retail-points" element={<RetailPointsPage />} />
|
||||||
<Route path="/catalog/countries" element={<CountriesPage />} />
|
|
||||||
<Route path="/inventory/stock" element={<StockPage />} />
|
<Route path="/inventory/stock" element={<StockPage />} />
|
||||||
<Route path="/inventory/movements" element={<StockMovementsPage />} />
|
<Route path="/inventory/movements" element={<StockMovementsPage />} />
|
||||||
<Route path="/purchases/supplies" element={<SuppliesPage />} />
|
<Route path="/purchases/supplies" element={<SuppliesPage />} />
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { logout } from '@/lib/auth'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import {
|
import {
|
||||||
LayoutDashboard, Package, FolderTree, Ruler, Tag,
|
LayoutDashboard, Package, FolderTree, Ruler, Tag,
|
||||||
Users, Warehouse, Store as StoreIcon, Globe, LogOut, Download, UserCog, Shield, ShieldCheck,
|
Users, Warehouse, Store as StoreIcon, LogOut, Download, UserCog, Shield, ShieldCheck,
|
||||||
Boxes, History, TruckIcon, ShoppingCart, Settings, Menu, X,
|
Boxes, History, TruckIcon, ShoppingCart, Settings, Menu, X,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { Logo } from './Logo'
|
import { Logo } from './Logo'
|
||||||
|
|
@ -49,9 +49,8 @@ function buildNav(isSuperAdmin: boolean): NavSection[] {
|
||||||
{ group: 'Продажи', items: [
|
{ group: 'Продажи', items: [
|
||||||
{ to: '/sales/retail', icon: ShoppingCart, label: 'Розничные чеки' },
|
{ to: '/sales/retail', icon: ShoppingCart, label: 'Розничные чеки' },
|
||||||
]},
|
]},
|
||||||
{ group: 'Справочники', items: [
|
// Справочники типа «Страны» — глобальные, управляются SuperAdmin'ом
|
||||||
{ to: '/catalog/countries', icon: Globe, label: 'Страны' },
|
// в системной консоли. В tenant-меню их больше нет.
|
||||||
]},
|
|
||||||
{ group: 'Импорт', items: [
|
{ group: 'Импорт', items: [
|
||||||
{ to: '/admin/import/moysklad', icon: Download, label: 'МойСклад' },
|
{ to: '/admin/import/moysklad', icon: Download, label: 'МойСклад' },
|
||||||
]},
|
]},
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { NavLink, Outlet, useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import {
|
import {
|
||||||
ShieldCheck, Building, FileClock, HeartPulse, HardDriveDownload,
|
ShieldCheck, Building, FileClock, HeartPulse, HardDriveDownload,
|
||||||
Settings, Users, LayoutDashboard, LogOut, Menu, X, ChevronDown,
|
Settings, Users, LayoutDashboard, LogOut, Menu, X, ChevronDown, Globe,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { api, getOrgOverride, setOrgOverride } from '@/lib/api'
|
import { api, getOrgOverride, setOrgOverride } from '@/lib/api'
|
||||||
import { logout } from '@/lib/auth'
|
import { logout } from '@/lib/auth'
|
||||||
|
|
@ -20,6 +20,9 @@ const NAV: NavSection[] = [
|
||||||
{ to: '/super-admin/organizations', icon: Building, label: 'Организации' },
|
{ to: '/super-admin/organizations', icon: Building, label: 'Организации' },
|
||||||
{ to: '/super-admin/users', icon: Users, label: 'Пользователи', soon: true },
|
{ to: '/super-admin/users', icon: Users, label: 'Пользователи', soon: true },
|
||||||
]},
|
]},
|
||||||
|
{ group: 'Справочники', items: [
|
||||||
|
{ to: '/super-admin/countries', icon: Globe, label: 'Страны' },
|
||||||
|
]},
|
||||||
{ group: 'Аудит', items: [
|
{ group: 'Аудит', items: [
|
||||||
{ to: '/super-admin/audit-log', icon: FileClock, label: 'Журнал действий' },
|
{ to: '/super-admin/audit-log', icon: FileClock, label: 'Журнал действий' },
|
||||||
]},
|
]},
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ export function OrganizationSettingsPage() {
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-slate-500 -mt-2">
|
<p className="text-xs text-slate-500 -mt-2">
|
||||||
Валюта и ставка НДС берутся из страны (<strong>{form.countryCode}</strong>) —
|
Валюта и ставка НДС берутся из страны (<strong>{form.countryCode}</strong>) —
|
||||||
чтобы изменить, откройте справочник <a className="underline" href="/catalog/countries">Страны</a>.
|
чтобы изменить — обратитесь к администратору платформы (справочник стран управляется в системной консоли).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue