food-market/src/food-market.pos/Views/LoginView.axaml
nns ff69dec527 feat(pos): регистрация устройства на точке продаж + привязка кассира
- Сервер: /api/me отдаёт retailPointIds (точки, к которым привязан сотрудник).
- Касса: DeviceRegistration (чистая логика, 5 unit-тестов) — первый запуск:
  админ выбирает точку из списка активных (сохраняется на устройстве, kv);
  кассир на незарегистрированном устройстве → «обратитесь к администратору»;
  кассир, не привязанный к точке устройства → «вы не привязаны к этой кассе»;
  админ работает на любой точке. Смена точки — кнопка-сброс (новую выберет
  только админ). Экран входа: шаг выбора точки (ComboBox активных точек).
- Заменяет прежний «берётся первая активная» (ошибка при двух точках).
- Тесты: unit (5 решений гейта) + integration (/api/me retailPointIds кассира).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:24:51 +05:00

40 lines
2.9 KiB
XML

<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:foodmarket.Pos.ViewModels"
x:Class="foodmarket.Pos.Views.LoginView"
x:DataType="vm:LoginViewModel">
<Grid Background="#0f172a">
<Border Background="#1e293b" CornerRadius="12" Width="440" Padding="28" VerticalAlignment="Center" HorizontalAlignment="Center">
<StackPanel Spacing="14">
<TextBlock Text="FOOD MARKET" Foreground="#22c55e" FontWeight="Bold" FontSize="22" HorizontalAlignment="Center"/>
<TextBlock Text="Вход кассира" Foreground="#f8fafc" FontSize="18" HorizontalAlignment="Center"/>
<StackPanel Spacing="4">
<TextBlock Text="Адрес сервера" Foreground="#94a3b8"/>
<TextBox Classes="pos" Text="{Binding ServerUrl}" FontSize="16"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Логин кассира" Foreground="#94a3b8"/>
<TextBox Classes="pos" Text="{Binding Username}" FontSize="18"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="PIN / пароль" Foreground="#94a3b8"/>
<TextBox Classes="pos" Text="{Binding Pin}" PasswordChar="●" FontSize="18"/>
</StackPanel>
<TextBlock Text="{Binding Error}" Foreground="#fca5a5" TextWrapping="Wrap" IsVisible="{Binding Error, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<Button Classes="pos primary" HorizontalAlignment="Stretch" Height="56" FontSize="18" Content="Войти" Command="{Binding LoginCommand}" IsDefault="True" IsEnabled="{Binding !Busy}" IsVisible="{Binding !RequirePointSelection}"/>
<!-- Регистрация устройства: выбор точки продаж (только администратор) -->
<StackPanel Spacing="8" IsVisible="{Binding RequirePointSelection}">
<TextBlock Text="Регистрация кассы — выберите точку продаж:" Foreground="#f8fafc"/>
<ComboBox ItemsSource="{Binding Points}" SelectedItem="{Binding SelectedPoint}" HorizontalAlignment="Stretch" FontSize="16"/>
<Button Classes="pos primary" HorizontalAlignment="Stretch" Height="52" Content="Зарегистрировать на этой точке" Command="{Binding ConfirmPointCommand}"/>
</StackPanel>
<Button Classes="pos" HorizontalAlignment="Stretch" Height="40" FontSize="13" Command="{Binding ResetDeviceCommand}" ToolTip.Tip="Сбросить регистрацию устройства. Новую точку выберет только администратор.">
<TextBlock Text="Сменить точку продаж (админ)" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
</StackPanel>
</Border>
</Grid>
</UserControl>