Merge pull request #462 from dotnet/salvage-401

Update PointOfSale App
This commit is contained in:
Gerald Versluis 2024-04-05 08:42:51 +02:00 коммит произвёл GitHub
Родитель 6cfa4a472b e5aa6a3e01
Коммит 85644dceb7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
69 изменённых файлов: 1343 добавлений и 222 удалений

Просмотреть файл

@ -7,6 +7,7 @@ languages:
- xaml
products:
- dotnet-maui
- dotnet-core
urlFragment: apps-pointofsale
---
@ -20,6 +21,12 @@ This app demonstrates various techniques for building a desktop and mobile appli
![food-mobile-2](https://user-images.githubusercontent.com/41873/183740348-7f55d10d-8f79-4ee0-a71e-64b317cbd64f.png)
## Local Testing
If you want to test the full functionality of this app locally for iOS, make sure to rename the `Platforms/iOS/EntitlementsSample.plist` file to ``Platforms/iOS/Entitlements.plist`.
By doing so, you will need a matching provisioning profile in your Apple Developer accound in order for everything to work correctly.
### Credits
* Original design: https://www.uplabs.com/posts/foodos-food-point-of-sale
@ -30,4 +37,4 @@ This app demonstrates various techniques for building a desktop and mobile appli
* CommunityToolkit.Maui: https://github.com/CommunityToolkit/Maui
* SkiaSharp & Lottie: https://mono.github.io/SkiaSharp.Extended/api/ui-maui/#sklottieview
* MicroCharts: https://github.com/microcharts-dotnet/Microcharts
* Ril.BlazorSignatureCanvas: https://github.com/ResourceWare/Ril.BlazorSignatureCanvas
* Ril.BlazorSignatureCanvas: https://github.com/ResourceWare/Ril.BlazorSignatureCanvas

Просмотреть файл

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup Condition=" '$(RunConfiguration)' == 'https' " />
<PropertyGroup Condition=" '$(RunConfiguration)' == 'http' " />
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Datasync" Version="5.0.12" />
<PackageReference Include="Microsoft.AspNetCore.Datasync.EFCore" Version="5.0.12" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Web" Version="2.0.7-preview" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved.
// Licensed under the MIT License.
using System;
using Microsoft.AspNetCore.Datasync;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
//using TodoAppService.NET6.Db;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration);
builder.Services.AddAuthorization();
//var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
//if (connectionString == null)
//{
// throw new ApplicationException("DefaultConnection is not set");
//}
//builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
//builder.Services.AddDatasyncControllers();
var app = builder.Build();
// Initialize the database
//using (var scope = app.Services.CreateScope())
//{
// var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// await context.InitializeDatabaseAsync().ConfigureAwait(false);
//}
// Configure and run the web service.
app.UseAuthentication();
app.UseAuthorization();
//app.MapControllers();
app.Run();

Просмотреть файл

@ -0,0 +1,39 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:64228",
"sslPort": 44388
}
},
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5128",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7111;http://localhost:5128",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
{
"AzureAD": {
"Instance": "https://login.microsoftonline.com",
"ClientId": "4c5a1b86-86f9-431a-a99d-999024e16848",
"TenantId": "common"
},
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=TodoApp;Trusted_Connection=True"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

Просмотреть файл

@ -0,0 +1,164 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"skuName": {
"type": "string",
"defaultValue": "F1",
"allowedValues": [
"F1",
"D1",
"B1",
"B2",
"B3",
"S1",
"S2",
"S3",
"P1",
"P2",
"P3",
"P4"
],
"metadata": {
"description": "Describes plan's pricing tier and instance size. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/"
}
},
"skuCapacity": {
"type": "int",
"defaultValue": 1,
"maxValue": 3,
"minValue": 1,
"metadata": {
"description": "Describes plan's instance count"
}
},
"sqlAdministratorLogin": {
"type": "string",
"defaultValue": "appadmin",
"metadata": {
"description": "The admin user of the SQL Server"
}
},
"sqlPassword": {
"type": "secureString",
"metadata": {
"description": "The password of the admin user of the SQL Server"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {
"hostingPlanName": "[format('{0}hosting', uniqueString(resourceGroup().id))]",
"websiteName": "[format('{0}-service', uniqueString(resourceGroup().id))]",
"sqlserverName": "[format('{0}-dbserver', uniqueString(resourceGroup().id))]",
"databaseName": "quickstart"
},
"resources": [
{
"type": "Microsoft.Sql/servers",
"apiVersion": "2021-02-01-preview",
"name": "[variables('sqlserverName')]",
"location": "[parameters('location')]",
"properties": {
"administratorLogin": "[parameters('sqlAdministratorLogin')]",
"administratorLoginPassword": "[parameters('sqlPassword')]",
"version": "12.0"
}
},
{
"type": "Microsoft.Sql/servers/databases",
"apiVersion": "2021-02-01-preview",
"name": "[format('{0}/{1}', variables('sqlserverName'), variables('databaseName'))]",
"location": "[parameters('location')]",
"sku": {
"name": "Basic"
},
"properties": {
"collation": "SQL_Latin1_General_CP1_CI_AS",
"maxSizeBytes": 1073741824
},
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', variables('sqlserverName'))]"
]
},
{
"type": "Microsoft.Sql/servers/firewallRules",
"apiVersion": "2021-02-01-preview",
"name": "[format('{0}/{1}', variables('sqlserverName'), 'AllowAllWindowsAzureIps')]",
"properties": {
"endIpAddress": "0.0.0.0",
"startIpAddress": "0.0.0.0"
},
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', variables('sqlserverName'))]"
]
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2021-03-01",
"name": "[variables('hostingPlanName')]",
"location": "[parameters('location')]",
"tags": {
"displayName": "HostingPlan"
},
"sku": {
"name": "[parameters('skuName')]",
"capacity": "[parameters('skuCapacity')]"
},
"properties": {
"numberOfWorkers": 1
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2021-03-01",
"name": "[variables('websiteName')]",
"location": "[parameters('location')]",
"tags": {
"[format('hidden-related:{0}', resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName')))]": "empty",
"displayName": "Website"
},
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]"
]
},
{
"type": "Microsoft.Web/sites/config",
"apiVersion": "2021-03-01",
"name": "[format('{0}/{1}', variables('websiteName'), 'connectionstrings')]",
"properties": {
"DefaultConnection": {
"value": "[format('Data Source=tcp:{0},1433;Initial Catalog={1};User Id={2}@{3};Password={4};', reference(resourceId('Microsoft.Sql/servers', variables('sqlserverName'))).fullyQualifiedDomainName, variables('databaseName'), parameters('sqlAdministratorLogin'), reference(resourceId('Microsoft.Sql/servers', variables('sqlserverName'))).fullyQualifiedDomainName, parameters('sqlPassword'))]",
"type": "SQLAzure"
}
},
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', variables('sqlserverName'))]",
"[resourceId('Microsoft.Web/sites', variables('websiteName'))]"
]
}
],
"outputs": {
"backendUrl": {
"type": "string",
"value": "[concat('https://', variables('websiteName'), '.azurewebsites.net')]"
},
"sqlUsername": {
"type": "string",
"value": "[parameters('sqlAdministratorLogin')]"
},
"sqlPassword": {
"type": "string",
"value": "[parameters('sqlPassword')]"
}
}
}

Просмотреть файл

@ -5,6 +5,8 @@ VisualStudioVersion = 17.0.31611.283
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PointOfSale", "PointOfSale\PointOfSale.csproj", "{88F24BAD-E7CD-4D00-87F4-702FE9AA7077}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PointOfSale.API", "PointOfSale.API\PointOfSale.API.csproj", "{E093156C-2134-4AE3-8C14-96CE94725BBF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -17,6 +19,10 @@ Global
{88F24BAD-E7CD-4D00-87F4-702FE9AA7077}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88F24BAD-E7CD-4D00-87F4-702FE9AA7077}.Release|Any CPU.Build.0 = Release|Any CPU
{88F24BAD-E7CD-4D00-87F4-702FE9AA7077}.Release|Any CPU.Deploy.0 = Release|Any CPU
{E093156C-2134-4AE3-8C14-96CE94725BBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E093156C-2134-4AE3-8C14-96CE94725BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E093156C-2134-4AE3-8C14-96CE94725BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E093156C-2134-4AE3-8C14-96CE94725BBF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Просмотреть файл

@ -1,14 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug",
"type": "comet",
"request": "launch",
"preLaunchTask": "comet: Build"
}
]
}

Просмотреть файл

@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:PointOfSale.Pages"
xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:b="clr-namespace:PointOfSale.Common.Behaviors"
xmlns:v="clr-namespace:PointOfSale.Pages.Views"
x:Class="PointOfSale.AppShell"
FlyoutWidth="104"
@ -84,20 +85,18 @@
Margin="0,15,0,15"/>
<RadioButton Value="home" IsChecked="True" Grid.Row="1"
ToolTipProperties.Text="Home"
b:CursorBehavior.AttachBehavior="True"
CheckedChanged="OnMenuItemChanged">
<RadioButton.Content>
<Image Source="home.png">
<!--<Image.Behaviors>
<mct:IconTintColorBehavior
TintColor="{Binding IsChecked,
Source={RelativeSource AncestorType={x:Type RadioButton}, AncestorLevel=1,Mode=TemplatedParent},
Converter={StaticResource CheckedColorConverter}}"/>
</Image.Behaviors>-->
</Image>
</RadioButton.Content>
</RadioButton>
<RadioButton Value="dashboard" Grid.Row="2"
ToolTipProperties.Text="Dashboard"
CheckedChanged="OnMenuItemChanged">
<RadioButton.Content>
<Image Source="graph.png">
@ -113,6 +112,7 @@
</RadioButton>
<RadioButton Value="settings" Grid.Row="3"
ToolTipProperties.Text="Settings"
CheckedChanged="OnMenuItemChanged">
<RadioButton.Content>
<Image Source="setting.png">
@ -128,6 +128,7 @@
</RadioButton>
<RadioButton Value="discount" Grid.Row="4"
ToolTipProperties.Text="Discount"
CheckedChanged="OnMenuItemChanged">
<RadioButton.Content>
<Image Source="discount.png">
@ -143,6 +144,7 @@
</RadioButton>
<RadioButton Value="message" Grid.Row="5"
ToolTipProperties.Text="Messages"
CheckedChanged="OnMenuItemChanged">
<RadioButton.Content>
<Image Source="message.png">
@ -158,13 +160,16 @@
</RadioButton>
<RadioButton Value="notification" Grid.Row="6"
ToolTipProperties.Text="Notifications"
CheckedChanged="OnMenuItemChanged">
<RadioButton.Content>
<Image Source="notification.png" />
</RadioButton.Content>
</RadioButton>
<Image Source="logout.png" Margin="0,15,0,15" Grid.Row="7"/>
<Image Source="logout.png"
ToolTipProperties.Text="Logout"
Margin="0,15,0,15" Grid.Row="7"/>
</Grid>
</DataTemplate>
</Shell.FlyoutContentTemplate>

Просмотреть файл

@ -4,5 +4,7 @@
xmlns:p="clr-namespace:PointOfSale.Pages.Handheld"
x:Class="PointOfSale.AppShellMobile"
Title="AppShellMobile">
<ShellContent ContentTemplate="{DataTemplate p:MobileLoginPage}" Route="login"/>
<ShellContent ContentTemplate="{DataTemplate p:OrdersPage}" Route="orders"/>
</Shell>

Просмотреть файл

@ -18,5 +18,6 @@ public partial class AppShellMobile : Shell
Routing.RegisterRoute(nameof(PayPage), typeof(PayPage));
Routing.RegisterRoute(nameof(SignaturePage), typeof(SignaturePage));
Routing.RegisterRoute(nameof(ReceiptPage), typeof(ReceiptPage));
Routing.RegisterRoute(nameof(ScanPage), typeof(ScanPage));
}
}

Просмотреть файл

@ -0,0 +1,43 @@
using System;
using CommunityToolkit.Maui.Behaviors;
namespace PointOfSale.Common.Behaviors;
public partial class CursorBehavior : PlatformBehavior<View>
{
public static readonly BindableProperty AttachBehaviorProperty =
BindableProperty.CreateAttached("AttachBehavior", typeof(bool), typeof(CursorBehavior), false, propertyChanged: OnAttachBehaviorChanged);
public static bool GetAttachBehavior(BindableObject view)
{
return (bool)view.GetValue(AttachBehaviorProperty);
}
public static void SetAttachBehavior(BindableObject view, bool value)
{
view.SetValue(AttachBehaviorProperty, value);
}
static void OnAttachBehaviorChanged(BindableObject view, object oldValue, object newValue)
{
var btn = view as Button;
if (btn == null)
{
return;
}
bool attachBehavior = (bool)newValue;
if (attachBehavior)
{
btn.Behaviors.Add(new CursorBehavior());
}
else
{
var toRemove = btn.Behaviors.FirstOrDefault(b => b is CursorBehavior);
if (toRemove != null)
{
btn.Behaviors.Remove(toRemove);
}
}
}
}

Просмотреть файл

@ -0,0 +1,23 @@
using System;
namespace PointOfSale.Data;
public static class Constants
{
/// <summary>
/// The base URI for the Datasync service.
/// </summary>
public static string ServiceUri = "https://yojjaontbpmia-service.azurewebsites.net";
/// <summary>
/// The application (client) ID for the native app within Azure Active Directory
/// </summary>
public static string ApplicationId = "e8b7e84c-1cb6-4619-bee9-ace98d4211e5";
/// <summary>
/// The list of scopes to request
/// </summary>
public static string[] Scopes = new[]
{
"access_as_user"
};
}

Просмотреть файл

@ -0,0 +1,17 @@
using System;
namespace PointOfSale.Data
{
public class UserAccount
{
public string DisplayName { get; set; }
public string ID { get; set; }
public string UserPrincipalName { get; set; }
public UserAccount()
{
}
}
}

Просмотреть файл

@ -1,7 +1,11 @@
using CommunityToolkit.Maui;
using Microsoft.Maui.LifecycleEvents;
using Microsoft.Maui.Platform;
using MonkeyCache;
using MonkeyCache.FileStore;
using Plugin.Maui.KeyListener;
using SkiaSharp.Views.Maui.Controls.Hosting;
using ZXing.Net.Maui;
#if WINDOWS
using Microsoft.UI;
@ -22,14 +26,20 @@ public static class MauiProgram
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseBarcodeReader()
.UseMauiCommunityToolkit()
.UseSkiaSharp()
.UseKeyListener()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("opensans_semibold.ttf", "OpenSansSemiBold");
fonts.AddFont("fa_solid.ttf", "FontAwesome");
fonts.AddFont("fabmdl2.ttf", "Fabric");
})
.ConfigureMauiHandlers(handlers =>
{
ModifyEntry();
});
builder.Services.AddMauiBlazorWebView();
@ -56,7 +66,7 @@ public static class MauiProgram
});
#endif
ModifyEntry();
Barrel.ApplicationId = "com.simplyprofound.pointofsale";
return builder.Build();
}
@ -70,7 +80,9 @@ public static class MauiProgram
#elif IOS || MACCATALYST
handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
#elif WINDOWS
// how can I remove the bottom border of the Entry?
handler.PlatformView.FontWeight = Microsoft.UI.Text.FontWeights.Thin;
handler.PlatformView.BorderThickness = new Microsoft.UI.Xaml.Thickness(0);
#endif
});
}

Просмотреть файл

@ -0,0 +1,10 @@
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace PointOfSale;
public class AddToOrderMessage : ValueChangedMessage<Item>
{
public AddToOrderMessage(Item product) : base(product)
{
}
}

Просмотреть файл

@ -0,0 +1,12 @@
using System;
using CommunityToolkit.Mvvm.Messaging.Messages;
namespace PointOfSale.Messages;
public class DragProductMessage : ValueChangedMessage<bool>
{
public DragProductMessage(bool value) : base(value)
{
}
}

Просмотреть файл

@ -18,6 +18,7 @@ public static class AppData
new Item(){ Title = "Hot Tea", Price = 4.00, Quantity = 1, Category = ItemCategory.Beverages, Image = "tea.png"},
new Item(){ Title = "Coffee", Price = 4.00, Quantity = 1, Category = ItemCategory.Beverages, Image = "coffee.png"},
new Item(){ Title = "Milk", Price = 5.00, Quantity = 1, Category = ItemCategory.Beverages, Image = "milk.png"},
new Item(){ Title = "Juice", Price = 50.00, Quantity = 1, Category = ItemCategory.Beverages, Image = "juice.png"},
};
public static List<Order> Orders { get; set; } = GenerateOrders();

Просмотреть файл

@ -1,7 +1,6 @@
namespace PointOfSale.Models;
[INotifyPropertyChanged]
public partial class Item
public partial class Item : ObservableObject
{
[ObservableProperty]
string title;

Просмотреть файл

@ -3,8 +3,7 @@ using PointOfSale.Pages.Handheld;
namespace PointOfSale.Models;
[INotifyPropertyChanged]
public partial class Order
public partial class Order : ObservableObject
{
[ObservableProperty]
private int table;
@ -19,9 +18,9 @@ public partial class Order
{
get
{
var tot = items.Sum(i => (i.Price * i.Quantity));
if (tip != 0)
tot = tot + (tot * tip);
var tot = Items.Sum(i => (i.Price * i.Quantity));
if (Tip != 0)
tot = tot + (tot * Tip);
return tot.ToString("N2");
}
}
@ -48,7 +47,7 @@ public partial class Order
}
[RelayCommand]
private async void Pay()
private async Task Pay()
{
try
{

Просмотреть файл

@ -1,7 +1,6 @@
namespace PointOfSale.Pages;
[INotifyPropertyChanged]
public partial class DashboardViewModel
public partial class DashboardViewModel : ObservableObject
{
[RelayCommand]
async Task ViewAll()

Просмотреть файл

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:handheld="clr-namespace:PointOfSale.Pages.Handheld"
x:Class="PointOfSale.Pages.Handheld.MobileLoginPage"
x:DataType="handheld:MobileLoginViewModel"
Shell.NavBarIsVisible="false"
Title="Login">
<ContentPage.BindingContext>
<handheld:MobileLoginViewModel/>
</ContentPage.BindingContext>
<Grid RowDefinitions="*,140,60,*" RowSpacing="20">
<Image Source="jajangmyeon.png" HorizontalOptions="Center"
SemanticProperties.Description="Image of a bowl of noodles"
Grid.Row="1"/>
<Button
Text="Login"
Command="{Binding LoginCommand}"
MinimumWidthRequest="240"
Grid.Row="2"
SemanticProperties.Description="tap this button to login"
VerticalOptions="Center"
HorizontalOptions="Center" />
</Grid>
</ContentPage>

Просмотреть файл

@ -0,0 +1,16 @@

namespace PointOfSale.Pages.Handheld;
public partial class MobileLoginPage : ContentPage
{
public MobileLoginPage()
{
InitializeComponent();
}
protected override async void OnNavigatedTo(NavigatedToEventArgs args)
{
base.OnNavigatedTo(args);
}
}

Просмотреть файл

@ -0,0 +1,20 @@
namespace PointOfSale.Pages.Handheld;
public partial class MobileLoginViewModel : ObservableObject
{
[RelayCommand]
async Task Login()
{
await Shell.Current.GoToAsync("//orders");
}
// display the message
private async Task ShowMessage(string title, string message)
{
_ = App.Current.MainPage.Dispatcher.Dispatch(async () =>
{
await App.Current.MainPage.DisplayAlert(title, message, "OK").ConfigureAwait(false);
});
}
}

Просмотреть файл

@ -11,14 +11,14 @@
<handheld:OrderDetailsViewModel/>
</ContentPage.BindingContext>
<Grid RowDefinitions="120, *, 66" Margin="20">
<VerticalStackLayout VerticalOptions="Center" Spacing="8">
<Grid RowDefinitions="Auto, 400, 66" Margin="20">
<VerticalStackLayout VerticalOptions="Start" Spacing="8">
<Label Text="Current Order" Style="{StaticResource LargeTitle}"/>
<Label Text="{Binding Order.Table, StringFormat='TABLE {0}'}"/>
<BoxView Style="{StaticResource HRule}"/>
</VerticalStackLayout>
<CollectionView Grid.Row="1" ItemsSource="{Binding Order.Items}">
<CollectionView Grid.Row="1" ItemsSource="{Binding Order.Items}" VerticalScrollBarVisibility="Never" Background="Transparent">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Item">
<Grid ColumnDefinitions="160,60,*" ColumnSpacing="8" Margin="0,8" HeightRequest="70">
@ -38,9 +38,8 @@
</CollectionView.Header>
</CollectionView>
<Grid ColumnSpacing="20" ColumnDefinitions="*,*" Grid.Row="2">
<Button Text="Add" Grid.Column="0" Command="{Binding AddCommand}" Style="{StaticResource PrimaryButtonOutline}" HorizontalOptions="Fill"/>
<Button Text="Pay" Grid.Column="1" Command="{Binding PayCommand}" HorizontalOptions="Fill"/>
<Grid Grid.Row="2">
<Button Text="Pay" Command="{Binding PayCommand}" HorizontalOptions="Fill"/>
</Grid>
</Grid>

Просмотреть файл

@ -1,9 +1,8 @@
namespace PointOfSale.Pages.Handheld;
[INotifyPropertyChanged]
[QueryProperty("Order","Order")]
[QueryProperty("Added", "Added")]
public partial class OrderDetailsViewModel
public partial class OrderDetailsViewModel : ObservableObject
{
[ObservableProperty]
Order order;
@ -18,7 +17,7 @@ public partial class OrderDetailsViewModel
{
var navigationParameter = new Dictionary<string, object>
{
{ "Order", order }
{ "Order", Order }
};
await Shell.Current.GoToAsync($"{nameof(TipPage)}", navigationParameter);
}
@ -27,10 +26,4 @@ public partial class OrderDetailsViewModel
Debug.WriteLine(ex.Message);
}
}
[RelayCommand]
async Task Add()
{
// await Shell.Current.GoToAsync($"{nameof(ScanPage)}");
}
}

Просмотреть файл

@ -4,57 +4,148 @@
xmlns:models="clr-namespace:PointOfSale.Models"
xmlns:handheld="clr-namespace:PointOfSale.Pages.Handheld"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:ak="clr-namespace:AlohaKit.Animations;assembly=AlohaKit.Animations"
x:Class="PointOfSale.Pages.Handheld.OrdersPage"
Shell.NavBarIsVisible="False"
x:DataType="handheld:OrdersViewModel"
Title="OrdersPage">
<ContentPage.BindingContext>
<handheld:OrdersViewModel/>
</ContentPage.BindingContext>
<Grid RowDefinitions="120, *, 66" Margin="20">
<VerticalStackLayout VerticalOptions="Center" Spacing="8">
<Grid RowDefinitions="Auto, *, 66" Margin="20,20,20,0">
<VerticalStackLayout VerticalOptions="Start" Spacing="4">
<Label Text="Orders" Style="{StaticResource LargeTitle}"/>
<Label Text="{Binding Source={x:Static system:DateTime.Today}, StringFormat=Date: {0:dddd, MMMM dd yyyy}, FallbackValue='Hello'}"/>
<BoxView Style="{StaticResource HRule}"/>
</VerticalStackLayout>
<CollectionView Grid.Row="1" Grid.RowSpan="2" ItemsSource="{Binding Orders}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Order">
<Grid ColumnDefinitions="80,100,*" ColumnSpacing="8" Margin="0,8" HeightRequest="70">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PayCommand}"/>
</Grid.GestureRecognizers>
<Border Stroke="Transparent" StrokeThickness="1" VerticalOptions="Center" HorizontalOptions="Start"
HeightRequest="60" WidthRequest="60" Background="{x:Static models:Order.RandomBrush}"
>
<Border.StrokeShape>
<Ellipse Frame="0,0,60,60" />
</Border.StrokeShape>
<Grid>
<Label Text="{Binding Table}" TextColor="White" VerticalOptions="Center" HorizontalOptions="Center"/>
</Grid>
</Border>
<Label Text="{Binding Total, StringFormat='${0}'}" Grid.Column="1" VerticalOptions="Center"/>
<Label Text="{Binding Status}" Grid.Column="2" VerticalOptions="Center" HorizontalOptions="Start"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Header>
<Grid ColumnDefinitions="80,100,*" Margin="0,8" HeightRequest="40" ColumnSpacing="8">
<Grid Grid.Row="1"
ColumnDefinitions="Auto"
toolkit:StateContainer.CurrentState="{Binding PageCurrentState}">
<toolkit:StateContainer.StateViews>
<Grid ColumnDefinitions="80,100,*"
RowDefinitions="40,*"
Margin="0,8"
ColumnSpacing="8"
toolkit:StateView.StateKey="Loading">
<Label Text="Table" Grid.Column="0" VerticalOptions="Center" FontAttributes="Bold"/>
<Label Text="Total" Grid.Column="1" VerticalOptions="Center" FontAttributes="Bold"/>
<Label Text="Status" Grid.Column="2" VerticalOptions="Center" HorizontalOptions="Start" FontAttributes="Bold"/>
</Grid>
</CollectionView.Header>
</CollectionView>
<FlexLayout Grid.Row="2" AlignContent="SpaceAround" JustifyContent="SpaceAround"
<VerticalStackLayout Grid.Row="1" Grid.ColumnSpan="3" x:Name="ListPreview">
<BindableLayout.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>one</x:String>
<x:String>one</x:String>
<x:String>one</x:String>
<x:String>one</x:String>
<x:String>one</x:String>
<x:String>one</x:String>
<x:String>one</x:String>
<x:String>one</x:String>
</x:Array>
</BindableLayout.ItemsSource>
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="80,100,*" ColumnSpacing="8" Margin="0,8" HeightRequest="70">
<Border Stroke="Transparent" StrokeThickness="1" VerticalOptions="Center" HorizontalOptions="Start"
HeightRequest="60" WidthRequest="60" Background="{StaticResource NeutralLighter}">
<Border.StrokeShape>
<Ellipse Frame="0,0,60,60" />
</Border.StrokeShape>
<Grid>
<BoxView HeightRequest="30" HorizontalOptions="Fill" VerticalOptions="Center" Color="{StaticResource NeutralLighter}"/>
</Grid>
</Border>
<BoxView HeightRequest="30" Grid.Column="1" HorizontalOptions="Fill" VerticalOptions="Center" Color="{StaticResource NeutralLighter}"/>
<BoxView HeightRequest="30" Grid.Column="2" HorizontalOptions="Fill" VerticalOptions="Center" Color="{StaticResource NeutralLighter}"/>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
<VerticalStackLayout.Resources>
<ak:StoryBoard x:Key="LoadingBoard" Target="{x:Reference ListPreview}">
<ak:FadeToAnimation Opacity="0.5" Duration="500"/>
<ak:FadeToAnimation Opacity="1" Duration="500"/>
<ak:FadeToAnimation Opacity="0.5" Duration="500"/>
<ak:FadeToAnimation Opacity="1" Duration="500"/>
<ak:FadeToAnimation Opacity="0.5" Duration="500"/>
<ak:FadeToAnimation Opacity="1" Duration="500"/>
</ak:StoryBoard>
</VerticalStackLayout.Resources>
<VerticalStackLayout.Triggers>
<DataTrigger
TargetType="VerticalStackLayout"
Binding="{Binding PageCurrentState}"
Value="Loading">
<DataTrigger.EnterActions>
<ak:BeginAnimation
Animation="{StaticResource LoadingBoard}" />
</DataTrigger.EnterActions>
</DataTrigger>
</VerticalStackLayout.Triggers>
</VerticalStackLayout>
</Grid>
<CollectionView ItemsSource="{Binding Orders}"
VerticalOptions="Start"
HeightRequest="600"
VerticalScrollBarVisibility="Never"
toolkit:StateView.StateKey="Loaded">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Order">
<Grid ColumnDefinitions="80,100,*" ColumnSpacing="8" Margin="0,8" HeightRequest="70" >
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PayCommand}"/>
</Grid.GestureRecognizers>
<Border Stroke="Transparent" StrokeThickness="1" VerticalOptions="Center" HorizontalOptions="Start"
HeightRequest="60" WidthRequest="60" Background="{x:Static models:Order.RandomBrush}">
<Border.StrokeShape>
<Ellipse Frame="0,0,60,60" />
</Border.StrokeShape>
<Grid>
<Label Text="{Binding Table}" TextColor="White" VerticalOptions="Center" HorizontalOptions="Center"/>
</Grid>
</Border>
<Label Text="{Binding Total, StringFormat='${0}'}" Grid.Column="1" VerticalOptions="Center"/>
<Label Text="{Binding Status}" Grid.Column="2" VerticalOptions="Center" HorizontalOptions="Start"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Header>
<Grid ColumnDefinitions="80,100,*" Margin="0,8" HeightRequest="40" ColumnSpacing="8">
<Label Text="Table" Grid.Column="0" VerticalOptions="Center" FontAttributes="Bold"/>
<Label Text="Total" Grid.Column="1" VerticalOptions="Center" FontAttributes="Bold"/>
<Label Text="Status" Grid.Column="2" VerticalOptions="Center" HorizontalOptions="Start" FontAttributes="Bold"/>
</Grid>
</CollectionView.Header>
<CollectionView.Footer>
<Grid HeightRequest="66" ColumnSpacing="8">
<!-- buffer for the content to flow behind but above the tabs-->
</Grid>
</CollectionView.Footer>
</CollectionView>
</toolkit:StateContainer.StateViews>
</Grid>
<FlexLayout Grid.Row="2"
AlignContent="SpaceAround"
JustifyContent="SpaceAround"
Background="{StaticResource DarkBg1Transparent}">
<Image Source="home.png"/>
<Image Source="logout.png"/>
<ImageButton Source="logout.png"
Command="{Binding LogOutCommand}"
/>
</FlexLayout>
</Grid>

Просмотреть файл

@ -1,9 +1,11 @@
namespace PointOfSale.Pages.Handheld;

namespace PointOfSale.Pages.Handheld;
public partial class OrdersPage : ContentPage
{
public OrdersPage()
{
InitializeComponent();
}
}
}

Просмотреть файл

@ -1,13 +1,47 @@
using Microsoft.Extensions.Configuration;
using System.Net.Http.Headers;
using System.Reflection;
namespace PointOfSale.Pages.Handheld;
[INotifyPropertyChanged]
public partial class OrdersViewModel
public partial class OrdersViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<Order> _orders;
[ObservableProperty]
private string displayName;
[ObservableProperty]
private string displayEmail;
[ObservableProperty]
private ImageSource profilePhoto;
[ObservableProperty]
private string pageCurrentState = "Loading";
[RelayCommand]
public async Task LogOut()
{
var result = await App.Current.MainPage.DisplayAlert("", "Do you want to logout?", "Yes", "Ooops, no");
if (!result)
return;
await Shell.Current.GoToAsync("//login");
}
public OrdersViewModel()
{
_orders = new ObservableCollection<Order>(AppData.Orders);
DelayedLoad().ConfigureAwait(false);
}
private async Task DelayedLoad()
{
await Task.Delay(4000);
PageCurrentState = "Loaded";
}
}

Просмотреть файл

@ -13,7 +13,7 @@
<handheld:PayViewModel/>
</ContentPage.BindingContext>
<Grid RowDefinitions="120,*"
<Grid RowDefinitions="Auto,*"
RowSpacing="20"
Margin="20"
RadioButtonGroup.GroupName="PaymentMethod"
@ -21,7 +21,7 @@
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PayCommand}"/>
</Grid.GestureRecognizers>
<VerticalStackLayout VerticalOptions="Center" Spacing="8" Grid.ColumnSpan="2">
<VerticalStackLayout VerticalOptions="Start" Spacing="8" Grid.ColumnSpan="2">
<Label Text="Insert, Tap, Swipe" Style="{StaticResource LargeTitle}"/>
<BoxView Style="{StaticResource HRule}"/>
</VerticalStackLayout>

Просмотреть файл

@ -1,18 +1,17 @@
namespace PointOfSale.Pages.Handheld;
[INotifyPropertyChanged]
[QueryProperty("Order","Order")]
public partial class PayViewModel
public partial class PayViewModel : ObservableObject
{
[ObservableProperty]
Order order;
[RelayCommand]
async void Pay()
async Task Pay()
{
var navigationParameter = new Dictionary<string, object>
{
{ "Order", order }
{ "Order", Order }
};
await Shell.Current.GoToAsync($"{nameof(SignaturePage)}", navigationParameter);
}

Просмотреть файл

@ -22,14 +22,14 @@
</ContentPage.Resources>
<Grid RowDefinitions="120,180,40,120,120,*,66"
<Grid RowDefinitions="Auto,180,40,120,120,*,66"
RowSpacing="20"
ColumnDefinitions="*,*"
ColumnSpacing="20"
Margin="20"
RadioButtonGroup.GroupName="ReceiptOption"
>
<VerticalStackLayout VerticalOptions="Center" Spacing="8" Grid.ColumnSpan="2">
<VerticalStackLayout VerticalOptions="Start" Spacing="8" Grid.ColumnSpan="2">
<Label Text="Payment Successful" Style="{StaticResource LargeTitle}"/>
<Label Text="{Binding Order.Total, StringFormat='Your Bill: ${0}'}"/>
<BoxView Style="{StaticResource HRule}"/>

Просмотреть файл

@ -1,14 +1,13 @@
namespace PointOfSale.Pages.Handheld;
[INotifyPropertyChanged]
[QueryProperty("Order","Order")]
public partial class ReceiptViewModel
public partial class ReceiptViewModel : ObservableObject
{
[ObservableProperty]
Order order;
[RelayCommand]
async void Done()
async Task Done()
{
await Shell.Current.GoToAsync("///orders");
}

Просмотреть файл

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:z="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI"
xmlns:a="clr-namespace:PointOfSale"
xmlns:i="clr-namespace:Icons"
x:Class="PointOfSale.Pages.Handheld.ScanPage"
Shell.PresentationMode="ModalAnimated"
Title="Scan">
<Grid>
<z:CameraBarcodeReaderView
Grid.Row="0" Grid.RowSpan="3"
x:Name="barcodeView"
BarcodesDetected="BarcodesDetected"
/>
<Border WidthRequest="240" HeightRequest="240"
VerticalOptions="Center" HorizontalOptions="Center"
Stroke="White"
StrokeThickness="3">
<Border.StrokeShape>
<RoundRectangle CornerRadius="12"/>
</Border.StrokeShape>
</Border>
<ImageButton
HeightRequest="60" WidthRequest="60"
VerticalOptions="Start" HorizontalOptions="Start"
Clicked="ImageButton_Clicked"
Aspect="Center"
Source="{FontImage FontFamily=Fabric, Color=White, Glyph={x:Static i:FabIconFont.Back},Size=18}"/>
<!--<Grid
Grid.Row="3"
BackgroundColor="#aa000000"
Padding="20"
ColumnDefinitions="Auto,*,Auto">
<Button Text="🔄️" Grid.Column="0" BackgroundColor="#aa000000" CornerRadius="8" BorderColor="Black" Clicked="SwitchCameraButton_Clicked" />
<z:BarcodeGeneratorView
x:Name="barcodeGenerator"
Grid.Column="1"
HorizontalOptions="Center"
VerticalOptions="Center"
HeightRequest="100"
WidthRequest="100"
ForegroundColor="DarkBlue"
Format="QrCode"
Value="Bla"
BarcodeMargin="1" />
<Button Text="💡" Grid.Column="2" BackgroundColor="#aa000000" CornerRadius="8" BorderColor="Black" Clicked="TorchButton_Clicked" />
</Grid>-->
</Grid>
</ContentPage>

Просмотреть файл

@ -0,0 +1,52 @@
using PointOfSale.Models;
using ZXing.Net.Maui;
namespace PointOfSale.Pages.Handheld;
public partial class ScanPage : ContentPage
{
public ScanPage()
{
InitializeComponent();
barcodeView.Options = new BarcodeReaderOptions
{
Formats = BarcodeFormats.All,
AutoRotate = true,
Multiple = true
};
}
protected void BarcodesDetected(object sender, BarcodeDetectionEventArgs e)
{
foreach (var barcode in e.Results)
Console.WriteLine($"Barcodes: {barcode.Format} -> {barcode.Value}");
Dispatcher.DispatchAsync(async () =>
{
var r = e.Results.First();
await CloseModal(r.Value);
});
}
private async Task CloseModal(string value)
{
var item = new Item
{
Price = 0,
Quantity = 1,
Title = value
};
var navigationParameter = new Dictionary<string, object>
{
{ "Item", item }
};
await Shell.Current.GoToAsync("..", navigationParameter);
}
async void ImageButton_Clicked(System.Object sender, System.EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
}

Просмотреть файл

@ -10,11 +10,11 @@
<handheld:SignatureViewModel/>
</ContentPage.BindingContext>
<Grid RowDefinitions="120,180,60,*,66"
<Grid RowDefinitions="Auto,180,60,*,66"
RowSpacing="20"
Margin="20">
<VerticalStackLayout VerticalOptions="Center" Spacing="8">
<VerticalStackLayout VerticalOptions="Start" Spacing="8">
<Label Text="Add Signature" Style="{StaticResource LargeTitle}"/>
<BoxView Style="{StaticResource HRule}"/>
</VerticalStackLayout>

Просмотреть файл

@ -3,9 +3,8 @@ using PointOfSale.Messages;
namespace PointOfSale.Pages.Handheld;
[INotifyPropertyChanged]
[QueryProperty("Order","Order")]
public partial class SignatureViewModel
public partial class SignatureViewModel : ObservableObject
{
[ObservableProperty]
Order order;
@ -14,21 +13,23 @@ public partial class SignatureViewModel
async Task Done()
{
WeakReferenceMessenger.Default.Send<SaveSignatureMessage>(
new SaveSignatureMessage(order.Table)
new SaveSignatureMessage(Order.Table)
);
var navigationParameter = new Dictionary<string, object>
{
{ "Order", order }
{ "Order", Order }
};
await Shell.Current.GoToAsync($"{nameof(ReceiptPage)}", navigationParameter);
}
[RelayCommand]
void Clear()
Task Clear()
{
WeakReferenceMessenger.Default.Send<ClearSignatureMessage>(
new ClearSignatureMessage(true)
); ;
);
return Task.CompletedTask;
}
}

Просмотреть файл

@ -19,7 +19,7 @@
</Style>
</ContentPage.Resources>
<Grid RowDefinitions="120,160,160,160,*,66"
<Grid RowDefinitions="Auto,160,160,160,*,66"
RowSpacing="20"
ColumnDefinitions="*,*"
ColumnSpacing="20"
@ -27,7 +27,7 @@
RadioButtonGroup.GroupName="TipAmount"
RadioButtonGroup.SelectedValue="{Binding Tip}"
>
<VerticalStackLayout VerticalOptions="Center" Spacing="8" Grid.ColumnSpan="2">
<VerticalStackLayout VerticalOptions="Start" Spacing="8" Grid.ColumnSpan="2">
<Label Text="Add a Tip" Style="{StaticResource LargeTitle}"/>
<Label Text="{Binding Order.Total, StringFormat='Your Bill: ${0}'}"/>
<BoxView Style="{StaticResource HRule}"/>
@ -112,7 +112,8 @@
</Label>
</RadioButton.Content>
</RadioButton>
<Button Grid.Row="5" HorizontalOptions="Fill" Grid.ColumnSpan="2" Text="Continue" Command="{Binding ContinueCommand}"/>
<Button Grid.Row="5" HorizontalOptions="Fill" Grid.Column="0" Text="Add Coupon" Command="{Binding AddCommand}" Style="{StaticResource SecondaryButtonOutline}"/>
<Button Grid.Row="5" HorizontalOptions="Fill" Grid.Column="1" Text="Continue" Command="{Binding ContinueCommand}"/>
</Grid>
</ContentPage>

Просмотреть файл

@ -1,8 +1,7 @@
namespace PointOfSale.Pages.Handheld;
[INotifyPropertyChanged]
[QueryProperty("Order","Order")]
public partial class TipViewModel
public partial class TipViewModel : ObservableObject
{
[ObservableProperty]
Order order;
@ -12,17 +11,23 @@ public partial class TipViewModel
partial void OnTipChanged(double value)
{
order.Tip = value;
Order.Tip = value;
OnPropertyChanged(nameof(Order));
}
[RelayCommand]
async void Continue()
async Task Continue()
{
var navigationParameter = new Dictionary<string, object>
{
{ "Order", order }
{ "Order", Order }
};
await Shell.Current.GoToAsync($"{nameof(PayPage)}", navigationParameter);
}
[RelayCommand]
async Task Add()
{
await Shell.Current.GoToAsync($"{nameof(ScanPage)}");
}
}

Просмотреть файл

@ -6,11 +6,12 @@
xmlns:pages="clr-namespace:PointOfSale.Pages"
xmlns:m="clr-namespace:PointOfSale.Models"
xmlns:l="clr-namespace:CustomLayouts"
xmlns:b="clr-namespace:PointOfSale.Common.Behaviors"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
x:DataType="pages:HomeViewModel"
x:Class="PointOfSale.Pages.HomePage"
Shell.NavBarIsVisible="False"
Title="HomePage">
Shell.NavBarIsVisible="{OnPlatform False, WinUI=True}"
Title="">
<ContentPage.BindingContext>
<pages:HomeViewModel/>
@ -19,24 +20,35 @@
<ContentPage.MenuBarItems>
<MenuBarItem Text="File">
<MenuFlyoutItem Text="Preferences"
ParentChanged="MenuFlyoutItem_ParentChanged"
Command="{Binding PreferencesCommand}" />
</MenuBarItem>
<MenuBarItem Text="Products">
<MenuFlyoutItem Text="Add Product" Command="{Binding AddProductCommand}"
ParentChanged="MenuFlyoutItem_ParentChanged"/>
<MenuFlyoutItem Text="Add Product Category"/>
<MenuFlyoutItem Text="Add Product"
Command="{Binding AddProductCommand}">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator Key="A" Modifiers="Cmd,Shift"/>
<KeyboardAccelerator Key="A" Modifiers="Ctrl"/>
</MenuFlyoutItem.KeyboardAccelerators>
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="&#xECDC;"
Size="12"
Color="Black"
FontFamily="Fabric" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Add Product Category"
IconImageSource="{FontImage &#xF12B;, FontFamily=Fabric, Color=Black, Size=12}"
/>
</MenuBarItem>
</ContentPage.MenuBarItems>
<Grid RowDefinitions="100,70,*"
ColumnDefinitions="*,400"
Margin="24,24,0,0"
x:Name="PageGrid">
<Grid RowDefinitions="100,Auto,*"
RowSpacing="10"
ColumnDefinitions="{OnPlatform '*,400', WinUI='*,440'}"
Margin="24,0,0,0"
x:Name="PageGrid">
<ScrollView Grid.Row="2">
<Grid RowDefinitions="70,*">
<Label Text="Choose Dishes" Style="{StaticResource Title1}"/>
@ -47,34 +59,72 @@
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="m:Item">
<Grid RowDefinitions="30,230" WidthRequest="200">
<Grid.Behaviors>
<b:CursorBehavior/>
</Grid.Behaviors>
<Grid.GestureRecognizers>
<DragGestureRecognizer
DragStarting="OnDragStarting"
DropCompleted="OnDropCompleted"
/>
</Grid.GestureRecognizers>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DefaultStates">
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter
TargetName="CardBg"
Property="Border.Background"
Value="{StaticResource Primary}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter
TargetName="CardBg"
Property="Border.Background"
Value="{StaticResource DarkBg2Brush}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<FlyoutBase.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Edit Details"
CommandParameter="docs" />
<MenuFlyoutItem Text="Remove"
CommandParameter="eng" />
</MenuFlyout>
</FlyoutBase.ContextFlyout>
<Border Background="{StaticResource DarkBg2Brush}"
Stroke="{StaticResource DarkBg2Brush}"
StrokeThickness="1"
Grid.Row="1"
x:Name="CardBg"
>
<Border.StrokeShape>
<RoundRectangle CornerRadius="16"/>
</Border.StrokeShape>
</Border>
<VerticalStackLayout Grid.RowSpan="2" Margin="20,0,20,20"
<VerticalStackLayout Grid.RowSpan="2" Margin="20,0,20,10"
VerticalOptions="End"
Spacing="6">
<Label Text="{Binding Title}" HorizontalOptions="Center" Style="{StaticResource Title2}" HorizontalTextAlignment="Center"/>
<Label Text="{Binding Price, StringFormat='${0}'}" HorizontalOptions="Center"/>
<!--<Label Text="20 Bowls available" HorizontalOptions="Center" Style="{StaticResource Subhead}"/>-->
<Label Text="{Binding Price, StringFormat='{0:C}'}" HorizontalOptions="Center"/>
</VerticalStackLayout>
<Image
Grid.RowSpan="2" VerticalOptions="Start"
Source="{Binding Image, FallbackValue=food_01.png}" HorizontalOptions="Center"/>
Margin="0,-20,0,0"
Source="{Binding Image, FallbackValue=food_01.png}"
HorizontalOptions="Center"/>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</l:HorizontalWrapLayout>
</Grid>
</ScrollView>
@ -117,14 +167,13 @@
<!-- Tab Control -->
<Grid Grid.Row="1">
<BoxView Style="{StaticResource HRule}"
Margin="0,31,0,0"
VerticalOptions="Start"
Margin="0,0,0,0"
VerticalOptions="End"
/>
<HorizontalStackLayout Spacing="20"
RadioButtonGroup.GroupName="MenuCategories"
RadioButtonGroup.SelectedValue="{Binding Category}"
>
VerticalOptions="End">
<BindableLayout.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Noodles</x:String>
@ -140,6 +189,9 @@
<RadioButton.ControlTemplate>
<ControlTemplate>
<Grid RowDefinitions="30,4">
<Grid.Behaviors>
<b:CursorBehavior/>
</Grid.Behaviors>
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="CheckedStates">
@ -168,6 +220,32 @@
Value="Transparent"/>
</VisualState.Setters>
</VisualState>
<!-- <VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter
TargetName="TextLabel"
Property="Label.TextColor"
Value="{StaticResource Primary}"/>
<Setter
TargetName="Indicator"
Property="BoxView.Color"
Value="{StaticResource Primary}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter
TargetName="TextLabel"
Property="Label.TextColor"
Value="White"/>
<Setter
TargetName="Indicator"
Property="BoxView.Color"
Value="Transparent"/>
</VisualState.Setters>
</VisualState> -->
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
@ -181,11 +259,26 @@
</DataTemplate>
</BindableLayout.ItemTemplate>
</HorizontalStackLayout>
</Grid>
<v:OrderCartView Grid.Column="1" Grid.RowSpan="3"/>
<BoxView Color="Black"
Opacity="0"
Grid.RowSpan="3" Grid.ColumnSpan="2"
HorizontalOptions="Fill"
VerticalOptions="Fill"
Margin="-24"
InputTransparent="True"
x:Name="BlockScreen"
/>
<v:OrderCartView Grid.Column="1" Grid.RowSpan="3">
<v:OrderCartView.GestureRecognizers>
<DropGestureRecognizer
DragOver="OnDragOver"
Drop="OnDrop"/>
</v:OrderCartView.GestureRecognizers>
</v:OrderCartView>
</Grid>
</ContentPage>

Просмотреть файл

@ -1,21 +1,29 @@
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Platform;
using PointOfSale.Messages;
#if IOS || MACCATALYST
using UIKit;
#endif
namespace PointOfSale.Pages;
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
}
WeakReferenceMessenger.Default.Register<AddProductMessage>(this, (r, m) =>
{
protected override void OnNavigatedTo(NavigatedToEventArgs args)
{
base.OnNavigatedTo(args);
WeakReferenceMessenger.Default.Register<AddProductMessage>(this, (r, m) =>
{
NavSubContent(m.Value);
});
});
}
void MenuFlyoutItem_ParentChanged(System.Object sender, System.EventArgs e)
@ -26,7 +34,7 @@ public partial class HomePage : ContentPage
public void NavSubContent(bool show)
public async void NavSubContent(bool show)
{
var displayWidth = DeviceDisplay.Current.MainDisplayInfo.Width;
@ -37,8 +45,12 @@ public partial class HomePage : ContentPage
Grid.SetRowSpan(addForm, 3);
// translate off screen right
addForm.TranslationX = displayWidth - addForm.X;
addForm.TranslateTo(0, 0, 800, easing: Easing.CubicOut);
}
_ = addForm.TranslateTo(0, 0, 800, easing: Easing.CubicOut);
_ = BlockScreen.FadeTo(0.8, 800, easing: Easing.CubicOut);
BlockScreen.InputTransparent = false;
}
else
{
// remove the product window
@ -46,11 +58,91 @@ public partial class HomePage : ContentPage
var view = (AddProductView)PageGrid.Children.Where(v => v.GetType() == typeof(AddProductView)).SingleOrDefault();
var x = DeviceDisplay.Current.MainDisplayInfo.Width;
view.TranslateTo(displayWidth - view.X, 0, 800, easing: Easing.CubicIn);
_ = view.TranslateTo(displayWidth - view.X, 0, 800, easing: Easing.CubicIn);
if (view != null)
PageGrid.Children.Remove(view);
_ = BlockScreen.FadeTo(0, 800, easing: Easing.CubicOut);
BlockScreen.InputTransparent = true;
await Task.Delay(800);
if (view != null){
PageGrid.Children.Remove(view);
}
}
}
void OnDragStarting(object sender, DragStartingEventArgs e)
{
WeakReferenceMessenger.Default.Send<DragProductMessage>(new DragProductMessage(true));
Item item = (Item)(sender as Element).Parent.BindingContext;
e.Data.Properties.Add("Product", item);
var previewImage = string.Empty;
if(item.Title == "Soda") {
previewImage = "hunter.png";
} else if(item.Title == "Hot Tea") {
previewImage = "maddy.png";
} else if(item.Title == "Milk") {
previewImage = "sweeky.png";
} else if(item.Title == "Coffee") {
previewImage = "david.png";
} else if(item.Title == "Iced Tea") {
previewImage = "beth.png";
} else if(item.Title == "Juice") {
previewImage = "rachel.png";
} else {
return;
}
#if IOS || MACCATALYST
Func<UIKit.UIDragPreview> action = () =>
{
var image = UIImage.FromBundle(previewImage);
UIKit.UIImageView imageView = new UIKit.UIImageView(image);
imageView.ContentMode = UIKit.UIViewContentMode.Center;
imageView.Frame = new CoreGraphics.CGRect(0, 0, 250, 250);
return new UIKit.UIDragPreview(imageView);
};
e.PlatformArgs.SetPreviewProvider(action);
#endif
}
void OnDragOver(object sender, DragEventArgs e)
{
#if IOS || MACCATALYST
e.PlatformArgs.SetDropProposal(new UIKit.UIDropProposal(UIKit.UIDropOperation.Copy));
#endif
}
void OnDropCompleted(object sender, DropCompletedEventArgs e)
{
WeakReferenceMessenger.Default.Send<DragProductMessage>(new DragProductMessage(false));
}
void OnDrop(object sender, DropEventArgs e)
{
Item product = (Item)e.Data.Properties["Product"];
Debug.WriteLine($"{product.Title}");
WeakReferenceMessenger.Default.Send<AddToOrderMessage>(new AddToOrderMessage(product));
// Perform logic to take action based on retrieved value.
}
IDispatcherTimer timer;
void OnPointerPressed(object sender, PointerEventArgs e)
{
timer = Dispatcher.CreateTimer();
timer.Interval = TimeSpan.FromMilliseconds(2000);
timer.Tick += (s, e) =>
{
timer.Stop();
WeakReferenceMessenger.Default.Send<AddProductMessage>(new AddProductMessage(true));
};
}
void OnPointerReleased(object sender, PointerEventArgs e)
{
timer.Stop();
}
}

Просмотреть файл

@ -1,8 +1,9 @@
using System;
using PointOfSale.Messages;
namespace PointOfSale.Pages;
[INotifyPropertyChanged]
public partial class HomeViewModel
public partial class HomeViewModel : ObservableObject
{
[ObservableProperty]
ObservableCollection<Item> _products;
@ -10,10 +11,10 @@ public partial class HomeViewModel
[ObservableProperty]
string category = ItemCategory.Noodles.ToString();
partial void OnCategoryChanged(string cat)
partial void OnCategoryChanged(string value)
{
ItemCategory category = (ItemCategory)Enum.Parse(typeof(ItemCategory), cat);
_products = new ObservableCollection<Item>(
ItemCategory category = (ItemCategory)Enum.Parse(typeof(ItemCategory), value);
Products = new ObservableCollection<Item>(
AppData.Items.Where(x => x.Category == category).ToList()
);
OnPropertyChanged(nameof(Products));
@ -21,7 +22,7 @@ public partial class HomeViewModel
public HomeViewModel()
{
_products = new ObservableCollection<Item>(
Products = new ObservableCollection<Item>(
AppData.Items.Where(x=>x.Category == ItemCategory.Noodles).ToList()
);
}
@ -33,8 +34,8 @@ public partial class HomeViewModel
}
[RelayCommand]
async Task AddProduct()
void AddProduct()
{
MessagingCenter.Send<HomeViewModel, string>(this, "action", "add");
WeakReferenceMessenger.Default.Send<AddProductMessage>(new AddProductMessage(true));
}
}

Просмотреть файл

@ -1,7 +1,6 @@
namespace PointOfSale.Pages;
[INotifyPropertyChanged]
public partial class SettingsViewModel
public partial class SettingsViewModel : ObservableObject
{
[ObservableProperty]
ObservableCollection<Item> _products;

Просмотреть файл

@ -17,8 +17,8 @@
</Style>
</ContentView.Resources>
<Grid RowDefinitions="*,108">
<ScrollView>
<Grid RowDefinitions="*,108" ColumnDefinitions="*,*" ColumnSpacing="0">
<ScrollView Grid.ColumnSpan="2">
<VerticalStackLayout Spacing="20" Margin="24">
<Label Text="Add a Product" Style="{StaticResource Title1}" />
<BoxView Style="{StaticResource HRule}"/>
@ -96,7 +96,14 @@
</VerticalStackLayout>
</ScrollView>
<Button Text="Save" Grid.Row="1" Margin="24" Command="{Binding SaveCommand}"
<Button Text="Cancel"
Grid.Row="1" Grid.Column="0"
Margin="24,24,12,24"
Style="{StaticResource SecondaryButtonOutline}"
HorizontalOptions="Fill"
Command="{Binding CancelCommand}" />
<Button Text="Save" Grid.Row="1" Grid.Column="1" Margin="12,24,24,24" Command="{Binding SaveCommand}"
HorizontalOptions="Fill"
/>
</Grid>

Просмотреть файл

@ -1,9 +1,32 @@
namespace PointOfSale.Pages;
using System.Security.Cryptography;
using Plugin.Maui.KeyListener;
using PointOfSale.Messages;
public partial class AddProductView
namespace PointOfSale.Pages;
public partial class AddProductView : ContentView, IDisposable
{
KeyboardBehavior kb = new KeyboardBehavior();
public AddProductView()
{
InitializeComponent();
this.Behaviors.Add(kb);
kb.KeyUp += Kb_KeyUp;
}
private void Kb_KeyUp(object sender, KeyPressedEventArgs e)
{
if(e.Keys == KeyboardKeys.Escape)
{
(this.BindingContext as AddProductViewModel).CancelCommand.Execute(null);
}
}
public void Dispose()
{
kb.KeyUp -= Kb_KeyUp;
this.Behaviors.Remove(kb);
}
}

Просмотреть файл

@ -1,8 +1,9 @@
using System;
using PointOfSale.Messages;
namespace PointOfSale.Pages;
[INotifyPropertyChanged]
public partial class AddProductViewModel
public partial class AddProductViewModel : ObservableObject
{
[ObservableProperty]
Item item = new Item();
@ -17,13 +18,21 @@ public partial class AddProductViewModel
ImageSource image;
[RelayCommand]
async void Save()
void Save()
{
ItemCategory cat = (ItemCategory)Enum.Parse(typeof(ItemCategory), category);
item.Category = cat;
AppData.Items.Add(item);
ItemCategory cat = (ItemCategory)Enum.Parse(typeof(ItemCategory), Category);
Item.Category = cat;
AppData.Items.Add(Item);
MessagingCenter.Send<AddProductViewModel, string>(this, "action", "done");
WeakReferenceMessenger.Default.Send<AddProductMessage>(new AddProductMessage(false));
}
[RelayCommand]
Task Cancel()
{
WeakReferenceMessenger.Default.Send<AddProductMessage>(new AddProductMessage(false));
return Task.CompletedTask;
}
[RelayCommand]
@ -58,6 +67,7 @@ public partial class AddProductViewModel
catch (Exception ex)
{
// The user canceled or something went wrong
Console.WriteLine(ex);
}
return null;

Просмотреть файл

@ -12,19 +12,66 @@
<views:OrderCartViewModel/>
</ContentView.BindingContext>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DefaultStates">
<VisualState x:Name="AcceptDrop">
<VisualState.Setters>
<Setter
Property="Background"
Value="{StaticResource Primary}"/>
<Setter
TargetName="PlaceOrderButton"
Property="Button.IsEnabled"
Value="False"/>
<Setter
TargetName="OrderTypes"
Property="HorizontalStackLayout.IsVisible"
Value="False"/>
<Setter
TargetName="AddItemIcon"
Property="Image.IsVisible"
Value="True"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter
Property="Background"
Value="{StaticResource DarkBg2Brush}"/>
<Setter
TargetName="PlaceOrderButton"
Property="Button.IsEnabled"
Value="True"/>
<Setter
TargetName="OrderTypes"
Property="HorizontalStackLayout.IsVisible"
Value="True"/>
<Setter
TargetName="AddItemIcon"
Property="Image.IsVisible"
Value="False"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid RowDefinitions="*,108">
<ScrollView>
<VerticalStackLayout Margin="24" Spacing="20">
<Label
Text="Order #4773"
Style="{StaticResource Title1}" />
<HorizontalStackLayout Spacing="12">
<HorizontalStackLayout.Resources>
<HorizontalStackLayout Spacing="12"
x:Name="OrderTypes">
<HorizontalStackLayout.Resources>
<Style TargetType="RadioButton">
<Setter Property="ControlTemplate" Value="{StaticResource ButtonRadioTemplate}"/>
<Setter Property="HorizontalOptions" Value="Start"/>
</Style>
</HorizontalStackLayout.Resources>
<RadioButton Content="Dine In" IsChecked="True"/>
<RadioButton Content="Carry Out"/>
@ -39,7 +86,7 @@
<BoxView Style="{StaticResource HRule}"/>
<VerticalStackLayout Spacing="12"
BindableLayout.ItemsSource="{Binding Order.Items}">
BindableLayout.ItemsSource="{Binding Items}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="m:Item">
<Grid ColumnDefinitions="60*,20*,20*">
@ -53,7 +100,7 @@
<FormattedString>
<Span Text="{Binding Title}"/>
<Span Text="{x:Static system:Environment.NewLine}"/>
<Span TextColor="{StaticResource TextSecondary}" FontSize="12" Text="{Binding Price, StringFormat='${0}'}"/>
<Span TextColor="{StaticResource TextSecondary}" FontSize="12" Text="{Binding Price, StringFormat='{0:C}'}"/>
</FormattedString>
</Label.FormattedText>
</Label>
@ -61,8 +108,9 @@
<Border
Grid.Column="1"
HorizontalOptions="Start"
HeightRequest="44"
WidthRequest="44"
WidthRequest="46"
Background="{StaticResource DarkBg1Brush}"
Stroke="{StaticResource SecondaryBrush}"
StrokeThickness="1">
@ -92,8 +140,17 @@
</VerticalStackLayout>
</VerticalStackLayout>
</ScrollView>
<Button Text="Place Order" HorizontalOptions="Fill" Margin="24" Grid.Row="1"
<Button Text="Place Order"
x:Name="PlaceOrderButton"
HorizontalOptions="Fill" Margin="24" Grid.Row="1"
Command="{Binding PlaceOrderCommand}"/>
<Image x:Name="AddItemIcon"
Source="{FontImage &#xEAEE;, FontFamily=Fabric, Size=48}"
Aspect="AspectFit"
HorizontalOptions="Center"
VerticalOptions="End"
Margin="0,-100,0,0"
Grid.Row="0"/>
</Grid>
</ContentView>

Просмотреть файл

@ -1,9 +1,19 @@
namespace PointOfSale.Pages.Views;
using PointOfSale.Messages;
namespace PointOfSale.Pages.Views;
public partial class OrderCartView : ContentView
{
public OrderCartView()
{
InitializeComponent();
WeakReferenceMessenger.Default.Register<DragProductMessage>(this, (r, m) =>
{
if(m.Value)
VisualStateManager.GoToState(this, "AcceptDrop");
else
VisualStateManager.GoToState(this, "Normal");
});
}
}

Просмотреть файл

@ -1,20 +1,51 @@
using System;
namespace PointOfSale.Pages.Views;
[INotifyPropertyChanged]
public partial class OrderCartViewModel
public partial class OrderCartViewModel : ObservableObject
{
[ObservableProperty]
Order order;
[ObservableProperty]
ObservableCollection<Item> items;
int index = 0;
public OrderCartViewModel()
{
Order = AppData.Orders.First();
Items = new ObservableCollection<Item>(Order.Items);
WeakReferenceMessenger.Default.Register<AddToOrderMessage>(this, (r, m) =>
{
AddToOrder(m.Value);
Items = new ObservableCollection<Item>(Order.Items);
OnPropertyChanged(nameof(Items));
});
}
private void AddToOrder(Item item)
{
//if item is in the order alread, increment the quantity
var existing = Order.Items.Where(x => x.Title == item.Title).SingleOrDefault();
if (existing != null)
{
existing.Quantity++;
}
else
{
Order.Items.Add(item);
}
}
[RelayCommand]
async Task PlaceOrder()
{
await App.Current.MainPage.DisplayAlert("Not Implemented", "Wouldn't it be cool tho?", "Okay");
if(index < (AppData.Orders.Count - 1))
index++;
else
index = 0;
Order = AppData.Orders[index];
}
}

Просмотреть файл

@ -1,6 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.dot.pointofsale" android:versionCode="1" android:versionName="1.0.0">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" android:label="Food">
<activity android:name="microsoft.identity.client.BrowserTabActivity" android:configChanges="orientation|screenSize" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="msale8b7e84c-1cb6-4619-bee9-ace98d4211e5" android:host="auth" />
</intent-filter>
</activity>
<activity android:name="MauiAppBasic.Platforms.Android.Resources.MsalActivity" android:configChanges="orientation|screenSize" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="msale8b7e84c-1cb6-4619-bee9-ace98d4211e5" android:host="auth" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Просмотреть файл

@ -1,10 +1,17 @@
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using PointOfSale.Data;
namespace PointOfSale;
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using Foundation;
using UIKit;
namespace PointOfSale;
@ -6,4 +7,14 @@ namespace PointOfSale;
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
public override void BuildMenu(IUIMenuBuilder builder)
{
base.BuildMenu(builder);
var formatMenuIdentifier = UIMenuIdentifier.Format.GetConstant();
builder.RemoveMenu(formatMenuIdentifier);
var editIdentifier = UIMenuIdentifier.Edit.GetConstant();
builder.RemoveMenu(editIdentifier);
}
}

Просмотреть файл

@ -0,0 +1,38 @@
using System;
using AppKit;
using UIKit;
namespace PointOfSale.Common.Behaviors;
public partial class CursorBehavior
{
UIHoverGestureRecognizer _hoverRecognizer;
protected override void OnAttachedTo(View bindable, UIView platformView)
{
_hoverRecognizer = new UIHoverGestureRecognizer(HandlerHoverDetection);
platformView.AddGestureRecognizer(_hoverRecognizer);
base.OnAttachedTo(bindable, platformView);
}
protected override void OnDetachedFrom(View bindable, UIView platformView)
{
platformView.RemoveGestureRecognizer(_hoverRecognizer);
_hoverRecognizer.Dispose();
_hoverRecognizer = null;
base.OnDetachedFrom(bindable, platformView);
}
void HandlerHoverDetection(UIHoverGestureRecognizer args)
{
if (args.State == UIGestureRecognizerState.Began ||
args.State == UIGestureRecognizerState.Changed)
{
NSCursor.PointingHandCursor.Set();
}
else if (args.State == UIGestureRecognizerState.Ended)
{
NSCursor.ArrowCursor.Set();
}
}
}

Просмотреть файл

@ -7,9 +7,8 @@
IgnorableNamespaces="uap rescap">
<Identity
Name="8D1C6CD3-CA27-4254-B76F-15B91335C418"
Publisher="CN=User Name"
Version="1.0.0.0" />
Version="1.0.0.0" Name="a045a695-05e1-4353-a3d0-5cd58ec52a45"/>
<Properties>
<DisplayName>PointOfSale</DisplayName>

Просмотреть файл

@ -0,0 +1,11 @@
<!-- Rename this file to Entitlements.plist for the full functionality to work. However, this will require you to have a matching provisioning profile in your Apple Developer Account. -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.microsoft.adalcache</string>
</array>
</dict>
</plist>

Просмотреть файл

@ -30,5 +30,7 @@
<string>Assets.xcassets/appicon.appiconset</string>
<key>NSCameraUsageDescription</key>
<string>Barcode Scanning</string>
<key>UIRequiresPersistentWiFi</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -16,14 +16,13 @@
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
<!-- Display name -->
<ApplicationTitle>Food</ApplicationTitle>
<ApplicationTitle>Point of Sale</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>net.dot.pointofsale</ApplicationId>
<ApplicationId>com.simplyprofound.pointofsale</ApplicationId>
<!-- Versions -->
<ApplicationVersion>1</ApplicationVersion>
@ -40,18 +39,6 @@
<AssemblyName>Food</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-ios|AnyCPU'">
<CodesignProvision>dPOSDev</CodesignProvision>
<CodesignKey>Apple Development: Created via API (2NJFZDD9ZM)</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-ios|AnyCPU'">
<CodesignProvision>Automatic</CodesignProvision>
<CodesignKey>iPhone Developer</CodesignKey>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\appicon.svg" ForegroundFile="Resources\appiconfg.svg" Color="#252836" />
@ -76,11 +63,22 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Maui" Version="6.0.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="Microcharts.Maui" Version="1.0.0" />
<PackageReference Include="SkiaSharp.Extended.UI.Maui" Version="2.0.0-preview.86" />
<PackageReference Include="CommunityToolkit.Maui" Version="3.0.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="Microcharts" Version="0.9.5.9" />
<PackageReference Include="Redth.ZXing.Net.Maui" Version="0.1.0-preview.4" />
<PackageReference Include="SkiaSharp.Extended.UI.Maui" Version="2.0.0-preview.92" />
<PackageReference Include="Ril.BlazorSignatureCanvas" Version="0.1.0-alpha" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.48.1" />
<PackageReference Include="MonkeyCache" Version="2.0.0-beta" />
<PackageReference Include="MonkeyCache.FileStore" Version="2.0.0-beta" />
<PackageReference Include="AlohaKit.Animations" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="AsyncAwaitBestPractices" Version="6.0.6" />
<PackageReference Include="Plugin.Maui.KeyListener" Version="1.0.0-preview1" />
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="$(MauiVersion)" />
</ItemGroup>
</Project>

Двоичные данные
8.0/Apps/PointOfSale/src/PointOfSale/Resources/Images/beth.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 72 KiB

Двоичные данные
8.0/Apps/PointOfSale/src/PointOfSale/Resources/Images/david.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 70 KiB

Двоичные данные
8.0/Apps/PointOfSale/src/PointOfSale/Resources/Images/hunter.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 58 KiB

Двоичные данные
8.0/Apps/PointOfSale/src/PointOfSale/Resources/Images/juice.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 15 KiB

Двоичные данные
8.0/Apps/PointOfSale/src/PointOfSale/Resources/Images/maddy.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 72 KiB

Двоичные данные
8.0/Apps/PointOfSale/src/PointOfSale/Resources/Images/rachel.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 44 KiB

Двоичные данные
8.0/Apps/PointOfSale/src/PointOfSale/Resources/Images/sweeky.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 87 KiB

Просмотреть файл

@ -85,6 +85,7 @@
</VisualStateManager.VisualStateGroups>
<Label Text="{TemplateBinding Content}"
Margin="12,7"
HorizontalTextAlignment="{OnPlatform Start, WinUI=Center}"
x:Name="TextLabel" />
</Border>

Просмотреть файл

@ -1,12 +1,12 @@
@inherits LayoutComponentBase
<div class="page">
<main>
<SignatureCanvas class="my-special-styling-class"
@ref=signatureCanvas
width="400" height="200" />
</main>
</div>
@inherits LayoutComponentBase
<div class="page">
<main>
<SignatureCanvas class="my-special-styling-class"
@ref=signatureCanvas
width="400" height="200" />
</main>
</div>
@code {
private SignatureCanvas signatureCanvas;

Просмотреть файл

@ -4,6 +4,10 @@
flex-direction: column;
}
.my-special-styling-class {
background-color: #0a58ca;
}
main {
flex: 1;
}