first commit
commit
de280dab2d
|
|
@ -0,0 +1,389 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
.nuspec/
|
||||
.buildtasks/
|
||||
templatesTest/
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
/src/Controls/tests/TestCases.HostApp/MauiProgram.user.cs
|
||||
CustomAgentLogsTmp/
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
Generated/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
tools/**
|
||||
!tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
.DS_Store
|
||||
|
||||
# Android Studio
|
||||
.gradle/
|
||||
.idea/
|
||||
local.properties
|
||||
|
||||
# Directory Build overrides for local setups
|
||||
Directory.Build.Override.props
|
||||
|
||||
# Only the "snapshots" directory should be added to Git, not the "snapshots-diff" directory
|
||||
snapshots-diff/
|
||||
|
||||
#install of dotnet version
|
||||
/.dotnet
|
||||
.dotnet
|
||||
temp
|
||||
.packages
|
||||
/src/Templates/.tempTemplateOutput
|
||||
|
||||
# TypeScript source map files (generated artifacts)
|
||||
# Note: CSS map files in templates (e.g., bootstrap) are intentionally tracked
|
||||
*.js.map
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Application
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="FrymasterBadgeApp.App"
|
||||
>
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
|
||||
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
using System.Diagnostics;
|
||||
using FrymasterBadgeApp.Services;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Controls.PlatformConfiguration.WindowsSpecific;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
public partial class App : Microsoft.Maui.Controls.Application
|
||||
{
|
||||
private readonly SqlService _db;
|
||||
private readonly PrinterService _printerService;
|
||||
private Microsoft.Maui.Controls.TabbedPage _rootTabbedPage;
|
||||
|
||||
public App(SqlService db, PrinterService printerService)
|
||||
{
|
||||
InitializeComponent();
|
||||
_db = db;
|
||||
_printerService = printerService;
|
||||
|
||||
Debug.WriteLine("=== APP STARTUP ===");
|
||||
|
||||
_rootTabbedPage = new Microsoft.Maui.Controls.TabbedPage
|
||||
{
|
||||
Title = "Frymaster Badge System",
|
||||
BarBackgroundColor = Colors.SlateGray,
|
||||
BarTextColor = Colors.White,
|
||||
};
|
||||
|
||||
_rootTabbedPage
|
||||
.On<Microsoft.Maui.Controls.PlatformConfiguration.Windows>()
|
||||
.SetHeaderIconsEnabled(false);
|
||||
|
||||
var loadingPage = new Microsoft.Maui.Controls.ContentPage
|
||||
{
|
||||
Title = "Loading",
|
||||
BackgroundColor = Colors.DarkSlateBlue,
|
||||
Content = new StackLayout
|
||||
{
|
||||
VerticalOptions = LayoutOptions.Center,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
Children =
|
||||
{
|
||||
new ActivityIndicator
|
||||
{
|
||||
IsRunning = true,
|
||||
Color = Colors.White,
|
||||
Scale = 2,
|
||||
},
|
||||
new Microsoft.Maui.Controls.Label
|
||||
{
|
||||
Text = "Loading companies...",
|
||||
TextColor = Colors.White,
|
||||
FontSize = 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
_rootTabbedPage.Children.Add(loadingPage);
|
||||
MainPage = _rootTabbedPage;
|
||||
|
||||
Debug.WriteLine("App: Resources loaded, loading page added, TabbedPage set as MainPage");
|
||||
}
|
||||
|
||||
protected override async void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
Debug.WriteLine("App: OnStart - calling LoadTabsAsync");
|
||||
await LoadTabsAsync();
|
||||
}
|
||||
|
||||
private async Task LoadTabsAsync()
|
||||
{
|
||||
Debug.WriteLine("LoadTabsAsync: ENTERED");
|
||||
|
||||
try
|
||||
{
|
||||
Debug.WriteLine("LoadTabsAsync: Querying database for companies...");
|
||||
var companies = await Task.Run(() => _db.Query("SELECT * FROM dbo.Companies", null));
|
||||
|
||||
Debug.WriteLine($"LoadTabsAsync: Query returned {companies?.Count ?? 0} companies");
|
||||
|
||||
await MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
Debug.WriteLine("LoadTabsAsync: On main thread - delay");
|
||||
await Task.Delay(200);
|
||||
|
||||
Debug.WriteLine(
|
||||
$"LoadTabsAsync: Current children count before clear: {_rootTabbedPage.Children.Count}"
|
||||
);
|
||||
_rootTabbedPage.Children.Clear();
|
||||
Debug.WriteLine("LoadTabsAsync: Children cleared");
|
||||
|
||||
if (companies == null || !companies.Any())
|
||||
{
|
||||
Debug.WriteLine("LoadTabsAsync: No companies - adding setup page");
|
||||
|
||||
var setupPage = new Microsoft.Maui.Controls.ContentPage
|
||||
{
|
||||
Title = "Setup",
|
||||
BackgroundColor = Colors.Purple,
|
||||
Content = new StackLayout
|
||||
{
|
||||
VerticalOptions = LayoutOptions.Center,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
Children =
|
||||
{
|
||||
new Microsoft.Maui.Controls.Label
|
||||
{
|
||||
Text = "No companies found - click to manage",
|
||||
TextColor = Colors.White,
|
||||
FontSize = 24,
|
||||
},
|
||||
new Microsoft.Maui.Controls.Button
|
||||
{
|
||||
Text = "Manage Companies",
|
||||
Command = new Command(async () =>
|
||||
await Microsoft.Maui.Controls.Application.Current.MainPage.Navigation.PushAsync(
|
||||
new CompanyPage(_db)
|
||||
)
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
_rootTabbedPage.Children.Add(setupPage);
|
||||
Debug.WriteLine("LoadTabsAsync: Setup page added");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"LoadTabsAsync: Adding {companies.Count} real tabs");
|
||||
|
||||
int added = 0;
|
||||
foreach (var company in companies)
|
||||
{
|
||||
string companyName = company.GetValueOrDefault("Name")?.ToString() ?? "Unknown";
|
||||
Debug.WriteLine($"LoadTabsAsync: Processing company '{companyName}'");
|
||||
|
||||
var employeePage = new EmployeePage(_db, _printerService, company);
|
||||
employeePage.Title = companyName;
|
||||
employeePage.BackgroundColor = Colors.LightBlue; // Temporary for visibility
|
||||
|
||||
employeePage.ToolbarItems.Add(
|
||||
new ToolbarItem
|
||||
{
|
||||
Text = "Manage",
|
||||
Command = new Command(async () =>
|
||||
await employeePage.Navigation.PushAsync(new CompanyPage(_db))
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
_rootTabbedPage.Children.Add(employeePage);
|
||||
added++;
|
||||
Debug.WriteLine(
|
||||
$"LoadTabsAsync: Added tab '{companyName}' ({added}/{companies.Count})"
|
||||
);
|
||||
}
|
||||
|
||||
Debug.WriteLine($"LoadTabsAsync: Finished - {added} tabs added");
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"LoadTabsAsync: EXCEPTION - {ex.Message}");
|
||||
Debug.WriteLine(ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Window CreateWindow(IActivationState? activationState)
|
||||
{
|
||||
Debug.WriteLine("CreateWindow called");
|
||||
return new Window(MainPage!);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Shell
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:pages="clr-namespace:FrymasterBadgeApp"
|
||||
x:Class="FrymasterBadgeApp.AppShell"
|
||||
>
|
||||
<TabBar x:Name="MainTabBar" />
|
||||
|
||||
<ShellContent
|
||||
x:Name="SetupTab"
|
||||
Title="Setup"
|
||||
ContentTemplate="{DataTemplate pages:CompanyPage}"
|
||||
/>
|
||||
</Shell>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
using FrymasterBadgeApp.Services;
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
public partial class AppShell : Shell
|
||||
{
|
||||
private readonly SqlService _db;
|
||||
private readonly PrinterService _printerService;
|
||||
|
||||
public AppShell(SqlService db, PrinterService printerService)
|
||||
{
|
||||
InitializeComponent();
|
||||
_db = db;
|
||||
_printerService = printerService;
|
||||
|
||||
_ = LoadCompaniesAsync();
|
||||
}
|
||||
|
||||
private async Task LoadCompaniesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var companies = await Task.Run(() => _db.Query("SELECT * FROM dbo.Companies", null));
|
||||
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
if (companies == null || !companies.Any())
|
||||
{
|
||||
CurrentItem = SetupTab;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var company in companies)
|
||||
{
|
||||
string companyName = company.GetValueOrDefault("Name")?.ToString() ?? "Unknown";
|
||||
|
||||
var employeePage = new EmployeePage(_db, _printerService, company)
|
||||
{
|
||||
Title = companyName,
|
||||
};
|
||||
|
||||
var manageBtn = new ToolbarItem
|
||||
{
|
||||
Text = "Manage Companies",
|
||||
Order = ToolbarItemOrder.Primary,
|
||||
Command = new Command(async () =>
|
||||
await employeePage.Navigation.PushAsync(new CompanyPage(_db))
|
||||
),
|
||||
};
|
||||
employeePage.ToolbarItems.Add(manageBtn);
|
||||
|
||||
var tab = new Tab { Title = companyName };
|
||||
tab.Items.Add(new ShellContent { Content = employeePage });
|
||||
|
||||
MainTabBar.Items.Add(tab);
|
||||
}
|
||||
|
||||
if (MainTabBar.Items.Count > 0)
|
||||
CurrentItem = MainTabBar.Items[0];
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
await DisplayAlert("Error", $"Failed to load: {ex.Message}", "OK")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<?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"
|
||||
x:Class="FrymasterBadgeApp.CompanyPage"
|
||||
Title="Manage Companies"
|
||||
BackgroundColor="#f1f5f9"
|
||||
>
|
||||
<Grid ColumnDefinitions="350,*" Padding="20">
|
||||
<!-- Left: Company List -->
|
||||
<Frame
|
||||
BackgroundColor="White"
|
||||
BorderColor="#e2e8f0"
|
||||
CornerRadius="12"
|
||||
HasShadow="True"
|
||||
Padding="0"
|
||||
>
|
||||
<VerticalStackLayout>
|
||||
<Label
|
||||
Text="Companies"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
Margin="15"
|
||||
TextColor="#1e293b"
|
||||
/>
|
||||
<CollectionView
|
||||
x:Name="CompanyList"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="OnCompanySelected"
|
||||
Margin="10"
|
||||
>
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Frame BackgroundColor="#f8fafc" CornerRadius="8" Padding="12" Margin="5">
|
||||
<Label Text="{Binding [Name]}" FontSize="16" TextColor="#1e293b" />
|
||||
</Frame>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
</VerticalStackLayout>
|
||||
</Frame>
|
||||
|
||||
<!-- Right: Edit Form -->
|
||||
<ScrollView Grid.Column="1" Margin="30,0,0,0">
|
||||
<VerticalStackLayout Spacing="25" HorizontalOptions="Start" WidthRequest="700">
|
||||
<Button
|
||||
Text="+ Add New Company"
|
||||
Clicked="OnAddNewClicked"
|
||||
BackgroundColor="#10b981"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
CornerRadius="12"
|
||||
HeightRequest="50"
|
||||
/>
|
||||
|
||||
<Frame BackgroundColor="White" CornerRadius="16" HasShadow="True" Padding="30">
|
||||
<VerticalStackLayout Spacing="20">
|
||||
<Label Text="Company Logo" FontSize="16" FontAttributes="Bold" TextColor="#1e293b" />
|
||||
|
||||
<HorizontalStackLayout HorizontalOptions="Center" Spacing="20">
|
||||
<Frame BackgroundColor="#f1f5f9" CornerRadius="8" Padding="0">
|
||||
<Image
|
||||
x:Name="LogoPreview"
|
||||
HeightRequest="120"
|
||||
WidthRequest="240"
|
||||
Aspect="AspectFit"
|
||||
/>
|
||||
</Frame>
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<Button
|
||||
Text="Choose Logo Image"
|
||||
Clicked="OnSelectLogoClicked"
|
||||
BackgroundColor="#64748b"
|
||||
TextColor="White"
|
||||
CornerRadius="10"
|
||||
/>
|
||||
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="Company Name" TextColor="#475569" />
|
||||
<Entry x:Name="CompName" Placeholder="Enter name" />
|
||||
|
||||
<Label Text="Street Address" TextColor="#475569" />
|
||||
<Entry x:Name="CompAddress" Placeholder="Enter address" />
|
||||
|
||||
<Grid ColumnDefinitions="*,100,150" ColumnSpacing="15">
|
||||
<VerticalStackLayout Grid.Column="0">
|
||||
<Label Text="City" TextColor="#475569" />
|
||||
<Entry x:Name="CompCity" Placeholder="City" />
|
||||
</VerticalStackLayout>
|
||||
<VerticalStackLayout Grid.Column="1">
|
||||
<Label Text="State" TextColor="#475569" />
|
||||
<Entry x:Name="CompState" Placeholder="ST" />
|
||||
</VerticalStackLayout>
|
||||
<VerticalStackLayout Grid.Column="2">
|
||||
<Label Text="Zip" TextColor="#475569" />
|
||||
<Entry x:Name="CompZip" Placeholder="Zip" />
|
||||
</VerticalStackLayout>
|
||||
</Grid>
|
||||
</VerticalStackLayout>
|
||||
|
||||
<Grid ColumnDefinitions="*,*" ColumnSpacing="20">
|
||||
<Button
|
||||
Text="Save Company"
|
||||
Clicked="OnSaveClicked"
|
||||
BackgroundColor="#2563eb"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
CornerRadius="12"
|
||||
/>
|
||||
<Button
|
||||
Text="Delete Company"
|
||||
Clicked="OnDeleteClicked"
|
||||
BackgroundColor="#ef4444"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
CornerRadius="12"
|
||||
/>
|
||||
</Grid>
|
||||
</VerticalStackLayout>
|
||||
</Frame>
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
</Grid>
|
||||
</ContentPage>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
using FrymasterBadgeApp.Services;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
public partial class CompanyPage : ContentPage
|
||||
{
|
||||
private readonly SqlService _db;
|
||||
private Dictionary<string, object>? _selectedCompany;
|
||||
private string _tempLogoPath = "";
|
||||
|
||||
public CompanyPage(SqlService db)
|
||||
{
|
||||
InitializeComponent();
|
||||
_db = db;
|
||||
LoadCompanies();
|
||||
}
|
||||
|
||||
private string GetLogoDirectory() =>
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
|
||||
"FrymasterBadgeApp",
|
||||
"Logos"
|
||||
);
|
||||
|
||||
private void LoadCompanies() =>
|
||||
CompanyList.ItemsSource = _db.Query("SELECT * FROM Companies", null);
|
||||
|
||||
private void OnCompanySelected(object sender, SelectedItemChangedEventArgs e)
|
||||
{
|
||||
_selectedCompany = e.SelectedItem as Dictionary<string, object>;
|
||||
if (_selectedCompany == null)
|
||||
return;
|
||||
|
||||
CompName.Text = _selectedCompany["Name"]?.ToString();
|
||||
CompAddress.Text = _selectedCompany["Address"]?.ToString();
|
||||
_tempLogoPath = _selectedCompany.GetValueOrDefault("logo")?.ToString() ?? "";
|
||||
LogoPreview.Source = File.Exists(_tempLogoPath)
|
||||
? ImageSource.FromFile(_tempLogoPath)
|
||||
: null;
|
||||
}
|
||||
|
||||
private async void OnSelectLogoClicked(object sender, EventArgs e)
|
||||
{
|
||||
var result = await FilePicker.Default.PickAsync(
|
||||
new PickOptions { PickerTitle = "Select Logo", FileTypes = FilePickerFileType.Images }
|
||||
);
|
||||
if (result != null)
|
||||
{
|
||||
_tempLogoPath = result.FullPath;
|
||||
LogoPreview.Source = ImageSource.FromFile(_tempLogoPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSaveClicked(object sender, EventArgs e)
|
||||
{
|
||||
string logoDir = GetLogoDirectory();
|
||||
if (!Directory.Exists(logoDir))
|
||||
Directory.CreateDirectory(logoDir);
|
||||
|
||||
string finalLogoPath = _tempLogoPath;
|
||||
if (!string.IsNullOrEmpty(_tempLogoPath) && !_tempLogoPath.Contains(logoDir))
|
||||
{
|
||||
string dest = Path.Combine(logoDir, Path.GetFileName(_tempLogoPath));
|
||||
File.Copy(_tempLogoPath, dest, true);
|
||||
finalLogoPath = dest;
|
||||
}
|
||||
|
||||
if (_selectedCompany == null)
|
||||
{
|
||||
_db.Execute(
|
||||
"INSERT INTO Companies (Name, Address, logo) VALUES (@n, @a, @l)",
|
||||
new SqlParameter[]
|
||||
{
|
||||
new("@n", CompName.Text),
|
||||
new("@a", CompAddress.Text),
|
||||
new("@l", finalLogoPath),
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_db.Execute(
|
||||
"UPDATE Companies SET Name=@n, Address=@a, logo=@l WHERE ID=@id",
|
||||
new SqlParameter[]
|
||||
{
|
||||
new("@n", CompName.Text),
|
||||
new("@a", CompAddress.Text),
|
||||
new("@l", finalLogoPath),
|
||||
new("@id", _selectedCompany["ID"]),
|
||||
}
|
||||
);
|
||||
}
|
||||
LoadCompanies();
|
||||
}
|
||||
|
||||
private void OnAddNewClicked(object sender, EventArgs e)
|
||||
{
|
||||
_selectedCompany = null;
|
||||
CompName.Text = CompAddress.Text = "";
|
||||
LogoPreview.Source = null;
|
||||
}
|
||||
|
||||
private void OnDeleteClicked(
|
||||
object sender,
|
||||
EventArgs e
|
||||
) { /* Delete logic */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="page">
|
||||
<div class="sidebar">
|
||||
<NavMenu />
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div class="top-row px-4">
|
||||
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
||||
</div>
|
||||
|
||||
<article class="content px-4">
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
.page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #d6d5d5;
|
||||
justify-content: flex-end;
|
||||
height: 3.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
white-space: nowrap;
|
||||
margin-left: 1.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.top-row ::deep a:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 640.98px) {
|
||||
.top-row {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.page {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
height: 100vh;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.top-row.auth ::deep a:first-child {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.top-row, article {
|
||||
padding-left: 2rem !important;
|
||||
padding-right: 1.5rem !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">FrymasterBadgeApp</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
|
||||
|
||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
||||
<nav class="flex-column">
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="counter">
|
||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="weather">
|
||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
.navbar-toggler {
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
width: 3.5rem;
|
||||
height: 2.5rem;
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 1rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.navbar-toggler:checked {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
height: 3.5rem;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.bi {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
top: -1px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.bi-house-door-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-plus-square-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-list-nested-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep a {
|
||||
color: #d7d7d7;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep a:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-toggler:checked ~ .nav-scrollable {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
/* Never collapse the sidebar for wide screens */
|
||||
display: block;
|
||||
/* Allow sidebar to scroll for tall menus */
|
||||
height: calc(100vh - 3.5rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
@page "/counter"
|
||||
|
||||
<h1>Counter</h1>
|
||||
|
||||
<p role="status">Current count: @currentCount</p>
|
||||
|
||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
||||
|
||||
@code {
|
||||
private int currentCount = 0;
|
||||
|
||||
private void IncrementCount()
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
@page "/"
|
||||
|
||||
<h1>Hello, world!</h1>
|
||||
|
||||
Welcome to your new app.
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
@page "/weather"
|
||||
|
||||
<h1>Weather</h1>
|
||||
|
||||
<p>This component demonstrates showing data.</p>
|
||||
|
||||
@if (forecasts == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Temp. (C)</th>
|
||||
<th>Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var forecast in forecasts)
|
||||
{
|
||||
<tr>
|
||||
<td>@forecast.Date.ToShortDateString()</td>
|
||||
<td>@forecast.TemperatureC</td>
|
||||
<td>@forecast.TemperatureF</td>
|
||||
<td>@forecast.Summary</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
@code {
|
||||
private WeatherForecast[]? forecasts;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Simulate asynchronous loading to demonstrate a loading indicator
|
||||
await Task.Delay(500);
|
||||
|
||||
var startDate = DateOnly.FromDateTime(DateTime.Now);
|
||||
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
|
||||
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = startDate.AddDays(index),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = summaries[Random.Shared.Next(summaries.Length)]
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private class WeatherForecast
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
public int TemperatureC { get; set; }
|
||||
public string? Summary { get; set; }
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<Router AppAssembly="@typeof(MauiProgram).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
</Router>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using FrymasterBadgeApp
|
||||
@using FrymasterBadgeApp.Components
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
<?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"
|
||||
x:Class="FrymasterBadgeApp.EmployeePage"
|
||||
BackgroundColor="#0f172a"
|
||||
>
|
||||
<Grid RowDefinitions="80,*" ColumnDefinitions="400,*">
|
||||
<!-- Header -->
|
||||
<Border Grid.ColumnSpan="2" BackgroundColor="#1e293b" StrokeThickness="0" Padding="20,0">
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<Label
|
||||
Text="FRYMASTER BADGE SYSTEM"
|
||||
FontSize="20"
|
||||
FontAttributes="Bold"
|
||||
TextColor="White"
|
||||
VerticalOptions="Center"
|
||||
/>
|
||||
<SearchBar
|
||||
Grid.Column="1"
|
||||
x:Name="EmployeeSearchBar"
|
||||
Placeholder="Search employees..."
|
||||
TextColor="White"
|
||||
BackgroundColor="#334155"
|
||||
PlaceholderColor="#94a3b8"
|
||||
Margin="10,0"
|
||||
VerticalOptions="Center"
|
||||
TextChanged="OnSearchTextChanged"
|
||||
/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Left: Employee List -->
|
||||
<Border Grid.Row="1" BackgroundColor="#1e293b" StrokeThickness="0">
|
||||
<CollectionView
|
||||
x:Name="EmployeeList"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
BackgroundColor="Transparent"
|
||||
Margin="10"
|
||||
>
|
||||
<CollectionView.ItemsLayout>
|
||||
<LinearItemsLayout Orientation="Vertical" ItemSpacing="8" />
|
||||
</CollectionView.ItemsLayout>
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
BackgroundColor="#334155"
|
||||
Padding="15"
|
||||
StrokeShape="RoundRectangle 8"
|
||||
Shadow="Shadow 0 4 8 0.2 Black"
|
||||
>
|
||||
<Label
|
||||
Text="{Binding [Data2]}"
|
||||
TextColor="White"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
</Border>
|
||||
|
||||
<!-- Right: Preview & Controls -->
|
||||
<ScrollView Grid.Row="1" Grid.Column="1" Padding="40,20">
|
||||
<VerticalStackLayout Spacing="25" HorizontalOptions="Center">
|
||||
<!-- Company Logo (used in badge preview) -->
|
||||
<Image
|
||||
x:Name="PreviewLogoFront"
|
||||
HeightRequest="60"
|
||||
Aspect="AspectFit"
|
||||
HorizontalOptions="Center"
|
||||
/>
|
||||
|
||||
<!-- Badge Type & Guest Selector -->
|
||||
<Border
|
||||
BackgroundColor="#1e293b"
|
||||
Padding="20"
|
||||
StrokeShape="RoundRectangle 12"
|
||||
Shadow="Shadow 0 4 10 0.3 Black"
|
||||
>
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<HorizontalStackLayout Spacing="15" HorizontalOptions="Center">
|
||||
<Label
|
||||
Text="Badge Type"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
FontSize="16"
|
||||
VerticalOptions="Center"
|
||||
/>
|
||||
<Picker
|
||||
x:Name="BadgeTypePicker"
|
||||
Title="Select type"
|
||||
WidthRequest="250"
|
||||
TextColor="White"
|
||||
BackgroundColor="#334155"
|
||||
SelectedIndexChanged="OnBadgeTypeChanged"
|
||||
>
|
||||
<Picker.Items>
|
||||
<x:String>Office</x:String>
|
||||
<x:String>Plant</x:String>
|
||||
<x:String>Maintenance</x:String>
|
||||
<x:String>Guest</x:String>
|
||||
<x:String>Visitor</x:String>
|
||||
<x:String>Vendor</x:String>
|
||||
</Picker.Items>
|
||||
</Picker>
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout
|
||||
x:Name="GuestImageSelector"
|
||||
IsVisible="False"
|
||||
Spacing="15"
|
||||
HorizontalOptions="Center"
|
||||
>
|
||||
<Label
|
||||
Text="Guest Image"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
FontSize="16"
|
||||
VerticalOptions="Center"
|
||||
/>
|
||||
<Picker
|
||||
x:Name="GuestImagePicker"
|
||||
Title="Select image"
|
||||
WidthRequest="250"
|
||||
TextColor="White"
|
||||
BackgroundColor="#334155"
|
||||
SelectedIndexChanged="OnGuestImageChanged"
|
||||
>
|
||||
<Picker.Items>
|
||||
<x:String>Guest1.png</x:String>
|
||||
<x:String>Guest2.png</x:String>
|
||||
<x:String>Guest3.png</x:String>
|
||||
</Picker.Items>
|
||||
</Picker>
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Badge Preview (Front + Back side-by-side) -->
|
||||
<Border
|
||||
x:Name="PreviewFrame"
|
||||
BackgroundColor="Transparent"
|
||||
Padding="30"
|
||||
StrokeShape="RoundRectangle 20"
|
||||
Shadow="Shadow 0 8 20 0.4 Black"
|
||||
HorizontalOptions="Center"
|
||||
>
|
||||
<!-- Content dynamically set in code-behind -->
|
||||
</Border>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<HorizontalStackLayout Spacing="20" HorizontalOptions="Center">
|
||||
<Button
|
||||
Text="📷 Edit / Capture Photo"
|
||||
Clicked="OnTakePhotoClicked"
|
||||
BackgroundColor="#3b82f6"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
CornerRadius="12"
|
||||
HeightRequest="55"
|
||||
WidthRequest="220"
|
||||
/>
|
||||
<Button
|
||||
Text="🖨️ Print Badge"
|
||||
Clicked="OnPrintClicked"
|
||||
BackgroundColor="#6366f1"
|
||||
TextColor="White"
|
||||
FontAttributes="Bold"
|
||||
CornerRadius="12"
|
||||
HeightRequest="55"
|
||||
WidthRequest="220"
|
||||
/>
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
</Grid>
|
||||
|
||||
<!-- Photo Editor Overlay -->
|
||||
<Grid x:Name="PhotoEditorOverlay" IsVisible="False" BackgroundColor="#CC000000">
|
||||
<Border
|
||||
BackgroundColor="#1e293b"
|
||||
Padding="30"
|
||||
StrokeShape="RoundRectangle 20"
|
||||
Shadow="Shadow 0 8 20 0.4 Black"
|
||||
HorizontalOptions="Center"
|
||||
VerticalOptions="Center"
|
||||
WidthRequest="500"
|
||||
>
|
||||
<VerticalStackLayout Spacing="25">
|
||||
<Label
|
||||
Text="Adjust Photo"
|
||||
FontSize="20"
|
||||
FontAttributes="Bold"
|
||||
TextColor="White"
|
||||
HorizontalTextAlignment="Center"
|
||||
/>
|
||||
|
||||
<Border
|
||||
BackgroundColor="Black"
|
||||
StrokeShape="RoundRectangle 12"
|
||||
Padding="0"
|
||||
IsClippedToBounds="True"
|
||||
HeightRequest="300"
|
||||
WidthRequest="300"
|
||||
>
|
||||
<Image x:Name="EditorPhotoPreview" Aspect="AspectFill">
|
||||
<Image.GestureRecognizers>
|
||||
<PanGestureRecognizer PanUpdated="OnPanUpdated" />
|
||||
</Image.GestureRecognizers>
|
||||
</Image>
|
||||
</Border>
|
||||
|
||||
<VerticalStackLayout Spacing="8">
|
||||
<Label Text="Zoom" TextColor="#cbd5e1" HorizontalTextAlignment="Center" />
|
||||
<Slider x:Name="ZoomSlider" Minimum="1" Maximum="4" Value="1" />
|
||||
</VerticalStackLayout>
|
||||
|
||||
<Grid ColumnDefinitions="*,*" ColumnSpacing="20">
|
||||
<Button
|
||||
Text="Cancel"
|
||||
Clicked="OnCancelPhoto"
|
||||
BackgroundColor="#ef4444"
|
||||
TextColor="White"
|
||||
CornerRadius="10"
|
||||
/>
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Text="Save Crop"
|
||||
Clicked="OnApplyPhoto"
|
||||
BackgroundColor="#22c55e"
|
||||
TextColor="White"
|
||||
CornerRadius="10"
|
||||
/>
|
||||
</Grid>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ContentPage>
|
||||
|
|
@ -0,0 +1,571 @@
|
|||
using System.IO; // Explicitly included for Path
|
||||
using FrymasterBadgeApp.Services;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Maui.Controls.Shapes; // For RoundRectangle
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
public partial class EmployeePage : ContentPage
|
||||
{
|
||||
private readonly SqlService _db;
|
||||
private readonly PrinterService _printerService;
|
||||
private Dictionary<string, object>? _selectedEmployee;
|
||||
private readonly Dictionary<string, object> _currentCompany;
|
||||
|
||||
private string _tempCapturePath = "";
|
||||
private double _editX = 0;
|
||||
private double _editY = 0;
|
||||
|
||||
private string _currentBadgeType = "Office";
|
||||
private string _currentGuestImage = "Guest1.png";
|
||||
|
||||
public EmployeePage(
|
||||
SqlService db,
|
||||
PrinterService printerService,
|
||||
Dictionary<string, object> company
|
||||
)
|
||||
{
|
||||
InitializeComponent();
|
||||
_db = db;
|
||||
_printerService = printerService;
|
||||
_currentCompany = company;
|
||||
|
||||
EditorPhotoPreview.SetBinding(
|
||||
Image.ScaleProperty,
|
||||
new Binding("Value", source: ZoomSlider)
|
||||
);
|
||||
|
||||
GuestImagePicker.Items.Add("Guest1.png");
|
||||
GuestImagePicker.Items.Add("Guest2.png");
|
||||
GuestImagePicker.Items.Add("Guest3.png");
|
||||
GuestImagePicker.SelectedIndex = 0;
|
||||
GuestImagePicker.SelectedIndexChanged += OnGuestImageChanged;
|
||||
|
||||
BadgeTypePicker.SelectedIndexChanged += OnBadgeTypeChanged;
|
||||
|
||||
LoadCompanyLogo();
|
||||
LoadEmployees();
|
||||
}
|
||||
|
||||
private string GetPhotoDir()
|
||||
{
|
||||
string path = @"C:\FrymasterData\photos";
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
private void LoadCompanyLogo()
|
||||
{
|
||||
string logoPath = _currentCompany.GetValueOrDefault("logo")?.ToString() ?? "";
|
||||
if (!string.IsNullOrEmpty(logoPath) && File.Exists(logoPath))
|
||||
{
|
||||
PreviewLogoFront.Source = ImageSource.FromFile(logoPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadEmployees()
|
||||
{
|
||||
string sql =
|
||||
"SELECT *, ISNULL(BadgeType, 'Office') AS BadgeType FROM dbo.tblData ORDER BY Data2";
|
||||
var results = _db.Query(sql, null);
|
||||
MainThread.BeginInvokeOnMainThread(() => EmployeeList.ItemsSource = results);
|
||||
}
|
||||
|
||||
private void OnSearchTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
string search = $"%{e.NewTextValue}%";
|
||||
string sql =
|
||||
@"SELECT *, ISNULL(BadgeType, 'Office') AS BadgeType
|
||||
FROM dbo.tblData
|
||||
WHERE Data2 LIKE @s OR Data1 LIKE @s
|
||||
ORDER BY Data2";
|
||||
var results = _db.Query(sql, new[] { new SqlParameter("@s", search) });
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
EmployeeList.SelectedItem = null;
|
||||
EmployeeList.ItemsSource = results;
|
||||
});
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
_selectedEmployee = e.CurrentSelection.FirstOrDefault() as Dictionary<string, object>;
|
||||
if (_selectedEmployee == null)
|
||||
{
|
||||
ClearBadgePreview();
|
||||
return;
|
||||
}
|
||||
|
||||
_currentBadgeType =
|
||||
_selectedEmployee.GetValueOrDefault("BadgeType")?.ToString() ?? "Office";
|
||||
BadgeTypePicker.SelectedItem = _currentBadgeType;
|
||||
GuestImageSelector.IsVisible = _currentBadgeType == "Guest";
|
||||
|
||||
RenderBadgePreview(_selectedEmployee);
|
||||
}
|
||||
|
||||
private void OnBadgeTypeChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_selectedEmployee == null)
|
||||
return;
|
||||
|
||||
_currentBadgeType = BadgeTypePicker.SelectedItem?.ToString() ?? "Office";
|
||||
GuestImageSelector.IsVisible = _currentBadgeType == "Guest";
|
||||
|
||||
string sql = "UPDATE dbo.tblData SET BadgeType = @type WHERE Data1 = @id";
|
||||
_db.Execute(
|
||||
sql,
|
||||
new[]
|
||||
{
|
||||
new SqlParameter("@type", _currentBadgeType),
|
||||
new SqlParameter("@id", _selectedEmployee["Data1"]),
|
||||
}
|
||||
);
|
||||
|
||||
_selectedEmployee["BadgeType"] = _currentBadgeType;
|
||||
RenderBadgePreview(_selectedEmployee);
|
||||
}
|
||||
|
||||
private void OnGuestImageChanged(object sender, EventArgs e)
|
||||
{
|
||||
_currentGuestImage = GuestImagePicker.SelectedItem?.ToString() ?? "Guest1.png";
|
||||
if (_selectedEmployee != null && _currentBadgeType == "Guest")
|
||||
RenderBadgePreview(_selectedEmployee);
|
||||
}
|
||||
|
||||
private void ClearBadgePreview()
|
||||
{
|
||||
PreviewFrame.Content = null;
|
||||
}
|
||||
|
||||
private void RenderBadgePreview(Dictionary<string, object> emp)
|
||||
{
|
||||
string firstName = (emp.GetValueOrDefault("Data8")?.ToString() ?? "").Trim();
|
||||
string lastName = (emp.GetValueOrDefault("Data7")?.ToString() ?? "").Trim();
|
||||
string recordNum = (emp.GetValueOrDefault("Data1")?.ToString() ?? "").Trim();
|
||||
string department = (emp.GetValueOrDefault("Data9")?.ToString() ?? "").Trim();
|
||||
string title = (emp.GetValueOrDefault("Data10")?.ToString() ?? "").Trim();
|
||||
string data5 = (emp.GetValueOrDefault("Data5")?.ToString() ?? "").Trim();
|
||||
|
||||
string fullName = $"{firstName} {lastName}".Trim();
|
||||
string barcode = $"*{recordNum}*";
|
||||
|
||||
string picCode = emp.GetValueOrDefault("picCode")?.ToString() ?? "";
|
||||
string photoPath = string.IsNullOrEmpty(picCode)
|
||||
? ""
|
||||
: System.IO.Path.Combine(GetPhotoDir(), $"{picCode}.jpg"); // Fully qualified
|
||||
|
||||
double scale = SafeToDouble(emp.GetValueOrDefault("cropScale"), 1.0);
|
||||
double tx = SafeToDouble(emp.GetValueOrDefault("cropX"), 0.0);
|
||||
double ty = SafeToDouble(emp.GetValueOrDefault("cropY"), 0.0);
|
||||
|
||||
bool medicalBlue = false;
|
||||
bool medicalRed = false;
|
||||
|
||||
var mainLayout = new HorizontalStackLayout
|
||||
{
|
||||
Spacing = 60,
|
||||
Padding = 20,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
};
|
||||
|
||||
// FRONT BADGE
|
||||
var frontLayout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
Color frontBg = Colors.White;
|
||||
Color frontBorderColor = Colors.Gray;
|
||||
|
||||
switch (_currentBadgeType)
|
||||
{
|
||||
case "Plant":
|
||||
frontBg = Color.FromArgb("#e8f5e8");
|
||||
frontBorderColor = Colors.Green;
|
||||
break;
|
||||
case "Maintenance":
|
||||
frontBg = Color.FromArgb("#fff3e0");
|
||||
frontBorderColor = Colors.Orange;
|
||||
break;
|
||||
case "Guest":
|
||||
frontBg = Color.FromArgb("#f3e8ff");
|
||||
frontBorderColor = Colors.Purple;
|
||||
break;
|
||||
case "Visitor":
|
||||
case "Vendor":
|
||||
frontBg = Color.FromArgb("#ffebee");
|
||||
frontBorderColor = Colors.Red;
|
||||
break;
|
||||
}
|
||||
|
||||
var frontBorder = new Border
|
||||
{
|
||||
Stroke = frontBorderColor,
|
||||
StrokeThickness = 4,
|
||||
BackgroundColor = frontBg,
|
||||
Padding = 25,
|
||||
WidthRequest = 380,
|
||||
StrokeShape = new RoundRectangle { CornerRadius = 20 },
|
||||
Shadow = new Shadow
|
||||
{
|
||||
Brush = SolidColorBrush.Black,
|
||||
Offset = new Point(0, 4),
|
||||
Radius = 10f,
|
||||
Opacity = 0.3f,
|
||||
},
|
||||
Content = frontLayout,
|
||||
};
|
||||
|
||||
frontLayout.Children.Add(
|
||||
new Image
|
||||
{
|
||||
Source = PreviewLogoFront.Source,
|
||||
HeightRequest = 70,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
Aspect = Aspect.AspectFit,
|
||||
}
|
||||
);
|
||||
|
||||
if (_currentBadgeType == "Guest")
|
||||
{
|
||||
frontLayout.Children.Add(
|
||||
new Image
|
||||
{
|
||||
Source = ImageSource.FromResource(
|
||||
$"FrymasterBadgeApp.Resources.Images.{_currentGuestImage}"
|
||||
),
|
||||
HeightRequest = 200,
|
||||
WidthRequest = 200,
|
||||
Aspect = Aspect.AspectFit,
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (File.Exists(photoPath))
|
||||
{
|
||||
var photoImg = new Image
|
||||
{
|
||||
Source = ImageSource.FromFile(photoPath),
|
||||
HeightRequest = 200,
|
||||
WidthRequest = 200,
|
||||
Aspect = Aspect.AspectFill,
|
||||
Scale = scale,
|
||||
TranslationX = tx,
|
||||
TranslationY = ty,
|
||||
};
|
||||
frontLayout.Children.Add(photoImg);
|
||||
}
|
||||
else
|
||||
{
|
||||
frontLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = "No Photo",
|
||||
FontSize = 18,
|
||||
TextColor = Colors.Gray,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
frontLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = fullName,
|
||||
FontSize = 24,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
frontLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = title,
|
||||
FontSize = 18,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
|
||||
if (!string.IsNullOrEmpty(department))
|
||||
frontLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = department,
|
||||
FontSize = 16,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
TextColor = Colors.Gray,
|
||||
}
|
||||
);
|
||||
|
||||
mainLayout.Children.Add(frontBorder);
|
||||
|
||||
// BACK BADGE
|
||||
var backLayout = new VerticalStackLayout { Spacing = 12 };
|
||||
|
||||
var backBorder = new Border
|
||||
{
|
||||
Stroke = Colors.Gray,
|
||||
StrokeThickness = 4,
|
||||
BackgroundColor = Colors.White,
|
||||
Padding = 25,
|
||||
WidthRequest = 380,
|
||||
StrokeShape = new RoundRectangle { CornerRadius = 20 },
|
||||
Shadow = new Shadow
|
||||
{
|
||||
Brush = SolidColorBrush.Black,
|
||||
Offset = new Point(0, 4),
|
||||
Radius = 10f,
|
||||
Opacity = 0.3f,
|
||||
},
|
||||
Content = backLayout,
|
||||
};
|
||||
|
||||
backLayout.Children.Add(
|
||||
new Image
|
||||
{
|
||||
Source = PreviewLogoFront.Source,
|
||||
HeightRequest = 50,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
Aspect = Aspect.AspectFit,
|
||||
}
|
||||
);
|
||||
|
||||
backLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text =
|
||||
$"This badge is the property of {_currentCompany.GetValueOrDefault("Name")} L.L.C.",
|
||||
FontSize = 14,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
|
||||
backLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = $"ID: {recordNum}",
|
||||
FontSize = 16,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
backLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = title,
|
||||
FontSize = 16,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
backLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = data5,
|
||||
FontSize = 16,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
|
||||
string address = _currentCompany.GetValueOrDefault("Address")?.ToString() ?? "";
|
||||
if (!string.IsNullOrEmpty(address))
|
||||
backLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = address,
|
||||
FontSize = 14,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
|
||||
var locationParts = new[]
|
||||
{
|
||||
_currentCompany.GetValueOrDefault("City")?.ToString(),
|
||||
_currentCompany.GetValueOrDefault("State")?.ToString(),
|
||||
_currentCompany.GetValueOrDefault("Zip")?.ToString(),
|
||||
}
|
||||
.Where(p => !string.IsNullOrEmpty(p))
|
||||
.ToArray();
|
||||
|
||||
if (locationParts.Length > 0)
|
||||
backLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = string.Join(", ", locationParts),
|
||||
FontSize = 14,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
|
||||
string phone = _currentCompany.GetValueOrDefault("Phone")?.ToString() ?? "";
|
||||
if (!string.IsNullOrEmpty(phone))
|
||||
backLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = phone,
|
||||
FontSize = 14,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
|
||||
if (medicalBlue || medicalRed)
|
||||
{
|
||||
var medStack = new HorizontalStackLayout
|
||||
{
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
Spacing = 10,
|
||||
};
|
||||
if (medicalBlue)
|
||||
medStack.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = "🩺",
|
||||
FontSize = 36,
|
||||
TextColor = Colors.Blue,
|
||||
}
|
||||
);
|
||||
if (medicalRed)
|
||||
medStack.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = "🩺",
|
||||
FontSize = 36,
|
||||
TextColor = Colors.Red,
|
||||
}
|
||||
);
|
||||
backLayout.Children.Add(medStack);
|
||||
}
|
||||
|
||||
if (_currentBadgeType == "Office" || _currentBadgeType == "Plant")
|
||||
{
|
||||
backLayout.Children.Add(
|
||||
new Label
|
||||
{
|
||||
Text = barcode,
|
||||
FontFamily = "BarcodeFont",
|
||||
FontSize = 50,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
TextColor = Colors.Black,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
mainLayout.Children.Add(backBorder);
|
||||
|
||||
PreviewFrame.Content = mainLayout;
|
||||
}
|
||||
|
||||
private async void OnTakePhotoClicked(object sender, EventArgs e)
|
||||
{
|
||||
if (_selectedEmployee == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var photo = await MediaPicker.Default.CapturePhotoAsync();
|
||||
if (photo != null)
|
||||
{
|
||||
_tempCapturePath = System.IO.Path.Combine(
|
||||
FileSystem.CacheDirectory,
|
||||
"temp_editor.jpg"
|
||||
); // Fully qualified
|
||||
using var source = await photo.OpenReadAsync();
|
||||
using var dest = File.OpenWrite(_tempCapturePath);
|
||||
await source.CopyToAsync(dest);
|
||||
|
||||
EditorPhotoPreview.Source = ImageSource.FromFile(_tempCapturePath);
|
||||
_editX = _editY = 0;
|
||||
EditorPhotoPreview.TranslationX = EditorPhotoPreview.TranslationY = 0;
|
||||
ZoomSlider.Value = 1;
|
||||
PhotoEditorOverlay.IsVisible = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await DisplayAlert("Camera Error", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
|
||||
{
|
||||
if (e.StatusType == GestureStatus.Running)
|
||||
{
|
||||
EditorPhotoPreview.TranslationX = _editX + e.TotalX;
|
||||
EditorPhotoPreview.TranslationY = _editY + e.TotalY;
|
||||
}
|
||||
else if (e.StatusType == GestureStatus.Completed)
|
||||
{
|
||||
_editX = EditorPhotoPreview.TranslationX;
|
||||
_editY = EditorPhotoPreview.TranslationY;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnApplyPhoto(object sender, EventArgs e)
|
||||
{
|
||||
if (_selectedEmployee == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
string firstName = (
|
||||
_selectedEmployee.GetValueOrDefault("Data8")?.ToString() ?? ""
|
||||
).Trim();
|
||||
string lastName = (
|
||||
_selectedEmployee.GetValueOrDefault("Data7")?.ToString() ?? ""
|
||||
).Trim();
|
||||
string recordNum = (
|
||||
_selectedEmployee.GetValueOrDefault("Data1")?.ToString() ?? ""
|
||||
).Trim();
|
||||
|
||||
string picCode = $"{lastName}{recordNum}{firstName}{lastName}";
|
||||
string finalPath = System.IO.Path.Combine(GetPhotoDir(), $"{picCode}.jpg"); // Fully qualified
|
||||
|
||||
File.Copy(_tempCapturePath, finalPath, true);
|
||||
|
||||
string sql =
|
||||
@"UPDATE dbo.tblData SET picCode = @pic, cropScale = @s, cropX = @x, cropY = @y WHERE Data1 = @id";
|
||||
|
||||
_db.Execute(
|
||||
sql,
|
||||
new[]
|
||||
{
|
||||
new SqlParameter("@pic", picCode),
|
||||
new SqlParameter("@s", ZoomSlider.Value),
|
||||
new SqlParameter("@x", EditorPhotoPreview.TranslationX),
|
||||
new SqlParameter("@y", EditorPhotoPreview.TranslationY),
|
||||
new SqlParameter("@id", recordNum),
|
||||
}
|
||||
);
|
||||
|
||||
_selectedEmployee["picCode"] = picCode;
|
||||
_selectedEmployee["cropScale"] = ZoomSlider.Value;
|
||||
_selectedEmployee["cropX"] = EditorPhotoPreview.TranslationX;
|
||||
_selectedEmployee["cropY"] = EditorPhotoPreview.TranslationY;
|
||||
|
||||
RenderBadgePreview(_selectedEmployee);
|
||||
PhotoEditorOverlay.IsVisible = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await DisplayAlert("Save Error", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCancelPhoto(object sender, EventArgs e) => PhotoEditorOverlay.IsVisible = false;
|
||||
|
||||
private double SafeToDouble(object? value, double defaultValue)
|
||||
{
|
||||
if (value == null || value == DBNull.Value)
|
||||
return defaultValue;
|
||||
return double.TryParse(value.ToString(), out double d) ? d : defaultValue;
|
||||
}
|
||||
|
||||
private void OnPrintClicked(object sender, EventArgs e)
|
||||
{
|
||||
if (_selectedEmployee != null)
|
||||
_printerService.PrintBadge(_selectedEmployee, _currentCompany, _currentBadgeType);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net9.0-windows10.0.19041.0</TargetFrameworks>
|
||||
<Platforms>x64</Platforms>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>FrymasterBadgeApp</RootNamespace>
|
||||
<UseMaui>true</UseMaui>
|
||||
<SingleProject>true</SingleProject>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<EnableDefaultCssItems>false</EnableDefaultCssItems>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<ApplicationTitle>FrymasterBadgeApp</ApplicationTitle>
|
||||
<ApplicationId>com.c4b3r.frymasterbadgeapp</ApplicationId>
|
||||
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
|
||||
<SupportedOSPlatformVersion
|
||||
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'"
|
||||
>10.0.17763.0</SupportedOSPlatformVersion
|
||||
>
|
||||
<TargetPlatformMinVersion
|
||||
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'"
|
||||
>10.0.17763.0</TargetPlatformMinVersion
|
||||
>
|
||||
|
||||
<NoWarn>$(NoWarn);MSB3277;NU1605</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<MauiIcon
|
||||
Include="Resources\AppIcon\appicon.svg"
|
||||
ForegroundFile="Resources\AppIcon\appiconfg.svg"
|
||||
Color="#512BD4"
|
||||
/>
|
||||
<MauiSplashScreen Include="Resources\Splash\splash.png" Color="#512BD4" BaseSize="128,128" />
|
||||
<MauiImage Include="Resources\Images\*" />
|
||||
<MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />
|
||||
<MauiFont Include="Resources\Fonts\*" />
|
||||
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
<EmbeddedResource Include="Resources\Raw\appsettings.json" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net9.0-windows10.0.19041.0'">
|
||||
<MauiEnableXamlC>false</MauiEnableXamlC>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.1" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Lextm.SharpSnmpLib" Version="12.5.6" />
|
||||
<PackageReference Include="SkiaSharp" Version="3.116.1" />
|
||||
<PackageReference Include="SkiaSharp.Views.Desktop.Common" Version="3.116.1" />
|
||||
<PackageReference Include="Neodynamic.SDK.BarcodeCore" Version="6.0.24.513" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="SdkApi.Card.Core">
|
||||
<HintPath>Libs\zebra\Sdk\SdkApi.Card.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SdkApi.Card.Desktop">
|
||||
<HintPath>Libs\zebra\Sdk\SdkApi.Card.Desktop.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SdkApi.Core">
|
||||
<HintPath>Libs\zebra\Sdk\SdkApi.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SdkApi.Desktop">
|
||||
<HintPath>Libs\zebra\Sdk\SdkApi.Desktop.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SdkApi.Desktop.Usb">
|
||||
<HintPath>Libs\zebra\Sdk\SdkApi.Desktop.Usb.dll</HintPath>
|
||||
</Reference>
|
||||
|
||||
<Content Include="Libs\zebra\Sdk\*.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Visible>false</Visible>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,384 @@
|
|||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v8.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v8.0": {
|
||||
"SdkApi.Card.Desktop/3.01.332": {
|
||||
"dependencies": {
|
||||
"Lextm.SharpSnmpLib": "12.5.2",
|
||||
"Microsoft.Extensions.DependencyModel": "6.0.0",
|
||||
"Microsoft.Windows.CsWinRT": "2.0.4",
|
||||
"Neodynamic.SDK.BarcodeCore": "6.0.24.513",
|
||||
"Newtonsoft.Json": "13.0.3",
|
||||
"Portable.BouncyCastle": "1.8.5.2",
|
||||
"SkiaSharp": "2.88.8",
|
||||
"SkiaSharp.Views.Desktop.Common": "2.88.8",
|
||||
"System.Drawing.Common": "8.0.1",
|
||||
"SdkApi.Card.Core": "3.1.332.0",
|
||||
"SdkApi.Core": "3.1.332.0",
|
||||
"SdkApi.Desktop": "3.1.332.0",
|
||||
"SdkApi.Desktop.Usb": "3.1.332.0",
|
||||
"runtimepack.Microsoft.Windows.SDK.NET.Ref": "10.0.17763.44"
|
||||
},
|
||||
"runtime": {
|
||||
"SdkApi.Card.Desktop.dll": {}
|
||||
}
|
||||
},
|
||||
"runtimepack.Microsoft.Windows.SDK.NET.Ref/10.0.17763.44": {
|
||||
"runtime": {
|
||||
"Microsoft.Windows.SDK.NET.dll": {
|
||||
"assemblyVersion": "10.0.17763.38",
|
||||
"fileVersion": "10.0.17763.43"
|
||||
},
|
||||
"WinRT.Runtime.dll": {
|
||||
"assemblyVersion": "2.1.0.0",
|
||||
"fileVersion": "2.1.2.47512"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Lextm.SharpSnmpLib/12.5.2": {
|
||||
"runtime": {
|
||||
"lib/net6.0/SharpSnmpLib.dll": {
|
||||
"assemblyVersion": "12.5.2.0",
|
||||
"fileVersion": "12.5.2.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyModel/6.0.0": {
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Memory": "4.5.4",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
|
||||
"System.Text.Encodings.Web": "6.0.0",
|
||||
"System.Text.Json": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Microsoft.Extensions.DependencyModel.dll": {
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/2.0.0": {},
|
||||
"Microsoft.Win32.SystemEvents/8.0.0": {},
|
||||
"Microsoft.Windows.CsWinRT/2.0.4": {
|
||||
"runtime": {
|
||||
"lib/net6.0/WinRT.Runtime.dll": {
|
||||
"assemblyVersion": "2.0.0.0",
|
||||
"fileVersion": "2.0.4.44211"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Neodynamic.SDK.BarcodeCore/6.0.24.513": {
|
||||
"dependencies": {
|
||||
"SkiaSharp": "2.88.8",
|
||||
"System.Text.Encoding.CodePages": "4.4.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Neodynamic.SDK.BarcodeCore.dll": {
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.24.513"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Newtonsoft.Json/13.0.3": {
|
||||
"runtime": {
|
||||
"lib/net6.0/Newtonsoft.Json.dll": {
|
||||
"assemblyVersion": "13.0.0.0",
|
||||
"fileVersion": "13.0.3.27908"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Portable.BouncyCastle/1.8.5.2": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/BouncyCastle.Crypto.dll": {
|
||||
"assemblyVersion": "1.8.5.0",
|
||||
"fileVersion": "1.8.5.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SkiaSharp/2.88.8": {
|
||||
"dependencies": {
|
||||
"SkiaSharp.NativeAssets.Win32": "2.88.8",
|
||||
"SkiaSharp.NativeAssets.macOS": "2.88.8"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/SkiaSharp.dll": {
|
||||
"assemblyVersion": "2.88.0.0",
|
||||
"fileVersion": "2.88.8.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SkiaSharp.NativeAssets.macOS/2.88.8": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/osx/native/libSkiaSharp.dylib": {
|
||||
"rid": "osx",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SkiaSharp.NativeAssets.Win32/2.88.8": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/win-arm64/native/libSkiaSharp.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x64/native/libSkiaSharp.dll": {
|
||||
"rid": "win-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x86/native/libSkiaSharp.dll": {
|
||||
"rid": "win-x86",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SkiaSharp.Views.Desktop.Common/2.88.8": {
|
||||
"dependencies": {
|
||||
"SkiaSharp": "2.88.8",
|
||||
"System.Drawing.Common": "8.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netcoreapp3.1/SkiaSharp.Views.Desktop.Common.dll": {
|
||||
"assemblyVersion": "2.88.0.0",
|
||||
"fileVersion": "2.88.8.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Buffers/4.5.1": {},
|
||||
"System.Drawing.Common/8.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.SystemEvents": "8.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/System.Drawing.Common.dll": {
|
||||
"assemblyVersion": "8.0.0.0",
|
||||
"fileVersion": "8.0.123.58006"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Memory/4.5.4": {},
|
||||
"System.Runtime.CompilerServices.Unsafe/6.0.0": {},
|
||||
"System.Text.Encoding.CodePages/4.4.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encodings.Web/6.0.0": {
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Json/6.0.0": {
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
|
||||
"System.Text.Encodings.Web": "6.0.0"
|
||||
}
|
||||
},
|
||||
"SdkApi.Card.Core/3.1.332.0": {
|
||||
"runtime": {
|
||||
"SdkApi.Card.Core.dll": {
|
||||
"assemblyVersion": "3.1.332.0",
|
||||
"fileVersion": "3.1.332.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SdkApi.Core/3.1.332.0": {
|
||||
"runtime": {
|
||||
"SdkApi.Core.dll": {
|
||||
"assemblyVersion": "3.1.332.0",
|
||||
"fileVersion": "3.1.332.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SdkApi.Desktop/3.1.332.0": {
|
||||
"runtime": {
|
||||
"SdkApi.Desktop.dll": {
|
||||
"assemblyVersion": "3.1.332.0",
|
||||
"fileVersion": "3.1.332.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SdkApi.Desktop.Usb/3.1.332.0": {
|
||||
"runtime": {
|
||||
"SdkApi.Desktop.Usb.dll": {
|
||||
"assemblyVersion": "3.1.332.0",
|
||||
"fileVersion": "3.1.332.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"SdkApi.Card.Desktop/3.01.332": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"runtimepack.Microsoft.Windows.SDK.NET.Ref/10.0.17763.44": {
|
||||
"type": "runtimepack",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Lextm.SharpSnmpLib/12.5.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-rWPyrB7klKxbuM/2wAQBgFk71XpERJt6uSRI27tCpMePidqbRD5whfFCXMeO0MlhUwqBmTZn7PoL6YnQLdZy1Q==",
|
||||
"path": "lextm.sharpsnmplib/12.5.2",
|
||||
"hashPath": "lextm.sharpsnmplib.12.5.2.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyModel/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==",
|
||||
"path": "microsoft.extensions.dependencymodel/6.0.0",
|
||||
"hashPath": "microsoft.extensions.dependencymodel.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/2.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-VdLJOCXhZaEMY7Hm2GKiULmn7IEPFE4XC5LPSfBVCUIA8YLZVh846gtfBJalsPQF2PlzdD7ecX7DZEulJ402ZQ==",
|
||||
"path": "microsoft.netcore.platforms/2.0.0",
|
||||
"hashPath": "microsoft.netcore.platforms.2.0.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents/8.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-9opKRyOKMCi2xJ7Bj7kxtZ1r9vbzosMvRrdEhVhDz8j8MoBGgB+WmC94yH839NPH+BclAjtQ/pyagvi/8gDLkw==",
|
||||
"path": "microsoft.win32.systemevents/8.0.0",
|
||||
"hashPath": "microsoft.win32.systemevents.8.0.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Windows.CsWinRT/2.0.4": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ULRbWAcqUG2jLZRGpMGOOL1SokFDcWlTLCjKUXVzQSnh/TR+7vsvet+eICRT0JIN4W2XRkJOBhxjFUEmlmN3rA==",
|
||||
"path": "microsoft.windows.cswinrt/2.0.4",
|
||||
"hashPath": "microsoft.windows.cswinrt.2.0.4.nupkg.sha512"
|
||||
},
|
||||
"Neodynamic.SDK.BarcodeCore/6.0.24.513": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-iNs7iD/lOnGE6qnG0v2bVn4/B4TWV3081fdW2bQjhr8Dj9ASLS+TVO8FUuvDz1ktTbtlhvKMFmsQdDgUwcNLyw==",
|
||||
"path": "neodynamic.sdk.barcodecore/6.0.24.513",
|
||||
"hashPath": "neodynamic.sdk.barcodecore.6.0.24.513.nupkg.sha512"
|
||||
},
|
||||
"Newtonsoft.Json/13.0.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==",
|
||||
"path": "newtonsoft.json/13.0.3",
|
||||
"hashPath": "newtonsoft.json.13.0.3.nupkg.sha512"
|
||||
},
|
||||
"Portable.BouncyCastle/1.8.5.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-iLfdr+T33QrDpsvcKRbMVk0PnjqE4hcrbgc6IH+A1BdPAThZlMB9s5TtFANhVVtdS+0oN3yIivgXRPRET7zuZw==",
|
||||
"path": "portable.bouncycastle/1.8.5.2",
|
||||
"hashPath": "portable.bouncycastle.1.8.5.2.nupkg.sha512"
|
||||
},
|
||||
"SkiaSharp/2.88.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-bRkp3uKp5ZI8gXYQT57uKwil1uobb2p8c69n7v5evlB/2JNcMAXVcw9DZAP5Ig3WSvgzGm2YSn27UVeOi05NlA==",
|
||||
"path": "skiasharp/2.88.8",
|
||||
"hashPath": "skiasharp.2.88.8.nupkg.sha512"
|
||||
},
|
||||
"SkiaSharp.NativeAssets.macOS/2.88.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-6Kn5TSkKlfyS6azWHF3Jk2sW5C4jCE5uSshM/5AbfFrR+5n6qM5XEnz9h4VaVl7LTxBvHvMkuPb/3bpbq0vxTw==",
|
||||
"path": "skiasharp.nativeassets.macos/2.88.8",
|
||||
"hashPath": "skiasharp.nativeassets.macos.2.88.8.nupkg.sha512"
|
||||
},
|
||||
"SkiaSharp.NativeAssets.Win32/2.88.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-O9QXoWEXA+6cweR4h3BOnwMz+pO9vL9mXdjLrpDd0w1QzCgWmLQBxa1VgySDITiH7nQndrDG1h6937zm9pLj1Q==",
|
||||
"path": "skiasharp.nativeassets.win32/2.88.8",
|
||||
"hashPath": "skiasharp.nativeassets.win32.2.88.8.nupkg.sha512"
|
||||
},
|
||||
"SkiaSharp.Views.Desktop.Common/2.88.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-SBP7byk9a5gmzHriOTC6BjykqJ5rCTqXIo/RLNszPaKwDs8naCZGUM9GZmreshPkpsyez3Ci4sm3+eleuSGgVQ==",
|
||||
"path": "skiasharp.views.desktop.common/2.88.8",
|
||||
"hashPath": "skiasharp.views.desktop.common.2.88.8.nupkg.sha512"
|
||||
},
|
||||
"System.Buffers/4.5.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==",
|
||||
"path": "system.buffers/4.5.1",
|
||||
"hashPath": "system.buffers.4.5.1.nupkg.sha512"
|
||||
},
|
||||
"System.Drawing.Common/8.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-x0rAZECxIGx/YVjN28YRdpqka0+H7YMN9741FUDzipXPDzesd60gef/LI0ZCOcYSDsacTLTHvMAvxHG+TjbNNQ==",
|
||||
"path": "system.drawing.common/8.0.1",
|
||||
"hashPath": "system.drawing.common.8.0.1.nupkg.sha512"
|
||||
},
|
||||
"System.Memory/4.5.4": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==",
|
||||
"path": "system.memory/4.5.4",
|
||||
"hashPath": "system.memory.4.5.4.nupkg.sha512"
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
|
||||
"path": "system.runtime.compilerservices.unsafe/6.0.0",
|
||||
"hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Encoding.CodePages/4.4.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-6JX7ZdaceBiLKLkYt8zJcp4xTJd1uYyXXEkPw6mnlUIjh1gZPIVKPtRXPmY5kLf6DwZmf5YLwR3QUrRonl7l0A==",
|
||||
"path": "system.text.encoding.codepages/4.4.0",
|
||||
"hashPath": "system.text.encoding.codepages.4.4.0.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Encodings.Web/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==",
|
||||
"path": "system.text.encodings.web/6.0.0",
|
||||
"hashPath": "system.text.encodings.web.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Json/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==",
|
||||
"path": "system.text.json/6.0.0",
|
||||
"hashPath": "system.text.json.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"SdkApi.Card.Core/3.1.332.0": {
|
||||
"type": "reference",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"SdkApi.Core/3.1.332.0": {
|
||||
"type": "reference",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"SdkApi.Desktop/3.1.332.0": {
|
||||
"type": "reference",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"SdkApi.Desktop.Usb/3.1.332.0": {
|
||||
"type": "reference",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
|
@ -0,0 +1,11 @@
|
|||
<?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"
|
||||
x:Class="FrymasterBadgeApp.MainPage"
|
||||
>
|
||||
<VerticalStackLayout VerticalOptions="Center" HorizontalOptions="Center" Spacing="20">
|
||||
<Label Text="Frymaster Badge System" FontSize="24" FontAttributes="Bold" TextColor="#1F2937" />
|
||||
<Label Text="Select a tab below to begin." TextColor="Gray" />
|
||||
</VerticalStackLayout>
|
||||
</ContentPage>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using FrymasterBadgeApp.Services;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
public partial class MainPage : ContentPage
|
||||
{
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
using System.Reflection;
|
||||
using FrymasterBadgeApp.Services;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
public static class MauiProgram
|
||||
{
|
||||
public static MauiApp CreateMauiApp()
|
||||
{
|
||||
var builder = MauiApp.CreateBuilder();
|
||||
builder
|
||||
.UseMauiApp<App>()
|
||||
.ConfigureFonts(fonts =>
|
||||
{
|
||||
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
|
||||
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
|
||||
fonts.AddFont("LibreBarcode128-Regular.ttf", "BarcodeFont");
|
||||
});
|
||||
|
||||
// --- Simplified Configuration Loading ---
|
||||
// Instead of GetManifestResourceStream, we use the simpler FileSystem approach
|
||||
// if the file is marked as a MauiAsset.
|
||||
try
|
||||
{
|
||||
var configPath = "appsettings.json";
|
||||
// Note: MauiAssets in Resources/Raw are accessed at the root of the app package
|
||||
using var stream = FileSystem.OpenAppPackageFileAsync(configPath).Result;
|
||||
if (stream != null)
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddJsonStream(stream).Build();
|
||||
builder.Configuration.AddConfiguration(config);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"CONFIG WARNING: {ex.Message}");
|
||||
// Don't 'throw' here unless it's truly fatal,
|
||||
// otherwise the app won't even start.
|
||||
}
|
||||
|
||||
// 1. Register Services
|
||||
builder.Services.AddSingleton<SqlService>();
|
||||
builder.Services.AddSingleton<PrinterService>();
|
||||
|
||||
// 2. Register Pages (Crucial for constructor injection)
|
||||
builder.Services.AddTransient<MainPage>();
|
||||
builder.Services.AddTransient<CompanyPage>();
|
||||
builder.Services.AddTransient<EmployeePage>();
|
||||
|
||||
#if DEBUG
|
||||
builder.Logging.AddDebug();
|
||||
#endif
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Models/CompanyModel.cs
|
||||
namespace FrymasterBadgeApp.Models
|
||||
{
|
||||
public class CompanyModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Address { get; set; } = string.Empty;
|
||||
public string City { get; set; } = string.Empty;
|
||||
public string State { get; set; } = string.Empty;
|
||||
public string Zip { get; set; } = string.Empty;
|
||||
public string Phone { get; set; } = string.Empty;
|
||||
|
||||
// This maps to the "LogoPath" column in your DB
|
||||
public string LogoPath { get; set; } = string.Empty;
|
||||
|
||||
// Helper property to check if a logo exists locally
|
||||
public bool HasLogo => !string.IsNullOrEmpty(LogoPath) && File.Exists(LogoPath);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// Models/PrintImageRequest.cs
|
||||
namespace FrymasterBadgeApp.Models
|
||||
{
|
||||
public class PrintImageRequest
|
||||
{
|
||||
// The fully rendered front of the badge (Photo + Name + Logo)
|
||||
public string FrontBase64 { get; set; } = string.Empty;
|
||||
|
||||
// Any instructions for the back (often a static disclaimer)
|
||||
public string BackBase64 { get; set; } = string.Empty;
|
||||
|
||||
// The IP Address or Name of the printer
|
||||
public string Printer { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?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>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
|
||||
public class MainActivity : MauiAppCompatActivity
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using Android.App;
|
||||
using Android.Runtime;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
[Application]
|
||||
public class MainApplication : MauiApplication
|
||||
{
|
||||
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
|
||||
: base(handle, ownership)
|
||||
{
|
||||
}
|
||||
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#512BD4</color>
|
||||
<color name="colorPrimaryDark">#2B0B98</color>
|
||||
<color name="colorAccent">#2B0B98</color>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Foundation;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : MauiUIApplicationDelegate
|
||||
{
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?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">
|
||||
<!-- See https://aka.ms/maui-publish-app-store#add-entitlements for more information about adding entitlements.-->
|
||||
<dict>
|
||||
<!-- App Sandbox must be enabled to distribute a MacCatalyst app through the Mac App Store. -->
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<!-- When App Sandbox is enabled, this value is required to open outgoing network connections. -->
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?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>
|
||||
<!-- The Mac App Store requires you specify if the app uses encryption. -->
|
||||
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/itsappusesnonexemptencryption -->
|
||||
<!-- <key>ITSAppUsesNonExemptEncryption</key> -->
|
||||
<!-- Please indicate <true/> or <false/> here. -->
|
||||
|
||||
<!-- Specify the category for your app here. -->
|
||||
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype -->
|
||||
<!-- <key>LSApplicationCategoryType</key> -->
|
||||
<!-- <string>public.app-category.YOUR-CATEGORY-HERE</string> -->
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/appicon.appiconset</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using ObjCRuntime;
|
||||
using UIKit;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
public class Program
|
||||
{
|
||||
// This is the main entry point of the application.
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// if you want to use a different Application Delegate class from "AppDelegate"
|
||||
// you can specify it here.
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using Microsoft.Maui;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
class Program : MauiApplication
|
||||
{
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var app = new Program();
|
||||
app.Run(args);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="maui-application-id-placeholder" version="0.0.0" api-version="9" xmlns="http://tizen.org/ns/packages">
|
||||
<profile name="common" />
|
||||
<ui-application appid="maui-application-id-placeholder" exec="FrymasterBadgeApp.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
|
||||
<label>maui-application-title-placeholder</label>
|
||||
<icon>maui-appicon-placeholder</icon>
|
||||
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
|
||||
</ui-application>
|
||||
<shortcut-list />
|
||||
<privileges>
|
||||
<privilege>http://tizen.org/privilege/internet</privilege>
|
||||
</privileges>
|
||||
<dependencies />
|
||||
<provides-appdefined-privileges />
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<maui:MauiWinUIApplication
|
||||
x:Class="FrymasterBadgeApp.WinUI.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:maui="using:Microsoft.Maui"
|
||||
xmlns:local="using:FrymasterBadgeApp.WinUI"
|
||||
></maui:MauiWinUIApplication>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.UI.Xaml;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace FrymasterBadgeApp.WinUI;
|
||||
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
public partial class App : MauiWinUIApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="6BFAE85A-04AF-446D-AD36-131E90DCA257" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>$placeholder$</DisplayName>
|
||||
<PublisherDisplayName>User Name</PublisherDisplayName>
|
||||
<Logo>$placeholder$.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="$placeholder$"
|
||||
Description="$placeholder$"
|
||||
Square150x150Logo="$placeholder$.png"
|
||||
Square44x44Logo="$placeholder$.png"
|
||||
BackgroundColor="transparent">
|
||||
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png" Square310x310Logo="$placeholder$.png" />
|
||||
<uap:SplashScreen Image="$placeholder$.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<DeviceCapability Name="webcam" />
|
||||
</Capabilities>
|
||||
|
||||
</Package>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="FrymasterBadgeApp.WinUI.app"/>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<!-- The combination of below two tags have the following effect:
|
||||
1) Per-Monitor for >= Windows 10 Anniversary Update
|
||||
2) System < Windows 10 Anniversary Update
|
||||
-->
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Foundation;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : MauiUIApplicationDelegate
|
||||
{
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?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>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/appicon.appiconset</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using ObjCRuntime;
|
||||
using UIKit;
|
||||
|
||||
namespace FrymasterBadgeApp;
|
||||
|
||||
public class Program
|
||||
{
|
||||
// This is the main entry point of the application.
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// if you want to use a different Application Delegate class from "AppDelegate"
|
||||
// you can specify it here.
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
This is the minimum required version of the Apple Privacy Manifest for .NET MAUI apps.
|
||||
The contents below are needed because of APIs that are used in the .NET framework and .NET MAUI SDK.
|
||||
|
||||
You are responsible for adding extra entries as needed for your application.
|
||||
|
||||
More information: https://aka.ms/maui-privacy-manifest
|
||||
-->
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>E174.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!--
|
||||
The entry below is only needed when you're using the Preferences API in your app.
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict> -->
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"profiles": {
|
||||
"Windows Machine": {
|
||||
"commandName": "Project",
|
||||
"nativeDebugging": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="456" height="456" fill="#512BD4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 228 B |
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"Default": "Server=ZABEASTPORTABLE\\SQLEXPRESS01;Database=FrymasterBadges;Trusted_Connection=True;TrustServerCertificate=True;"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<?xaml-comp compile="true" ?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
>
|
||||
<Color x:Key="Primary">#2563EB</Color>
|
||||
<Color x:Key="PrimaryDark">#1E40AF</Color>
|
||||
<Color x:Key="Secondary">#64748B</Color>
|
||||
<Color x:Key="Tertiary">#F1F5F9</Color>
|
||||
|
||||
<Color x:Key="Success">#10B981</Color>
|
||||
<Color x:Key="Danger">#EF4444</Color>
|
||||
<Color x:Key="Warning">#F59E0B</Color>
|
||||
<Color x:Key="Info">#3B82F6</Color>
|
||||
<Color x:Key="PageBgLight">#F9FAFB</Color>
|
||||
<Color x:Key="PageBgDark">#111827</Color>
|
||||
<Color x:Key="Black">#000000</Color>
|
||||
<Color x:Key="White">#FFFFFF</Color>
|
||||
<Color x:Key="Gray100">#F3F4F6</Color>
|
||||
<Color x:Key="Gray200">#E5E7EB</Color>
|
||||
<Color x:Key="Gray500">#6B7280</Color>
|
||||
<Color x:Key="Gray900">#111827</Color>
|
||||
|
||||
<Color x:Key="AppBackgroundColor">#FFFFFF</Color>
|
||||
<Color x:Key="NavigationPrimary">#2563EB</Color>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<?xaml-comp compile="true" ?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
>
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="TextColor" Value="{StaticResource Gray900}" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CardFrame" TargetType="Frame">
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource White}" />
|
||||
<Setter Property="BorderColor" Value="{StaticResource Gray200}" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
<Setter Property="HasShadow" Value="True" />
|
||||
<Setter Property="Padding" Value="15" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="EntryStyle" TargetType="Entry">
|
||||
<Setter Property="TextColor" Value="{StaticResource Gray900}" />
|
||||
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource Gray100}" />
|
||||
<Setter Property="Margin" Value="0,5" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="NavigationPage">
|
||||
<Setter Property="BarBackgroundColor" Value="{StaticResource Primary}" />
|
||||
<Setter Property="BarTextColor" Value="{StaticResource White}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
using System.Text;
|
||||
using SkiaSharp;
|
||||
using Zebra.Sdk.Card.Containers;
|
||||
using Zebra.Sdk.Card.Enumerations;
|
||||
using Zebra.Sdk.Card.Graphics;
|
||||
using Zebra.Sdk.Card.Printer;
|
||||
using Zebra.Sdk.Comm;
|
||||
using Zebra.Sdk.Printer.Discovery;
|
||||
|
||||
namespace FrymasterBadgeApp.Services;
|
||||
|
||||
public class PrinterService
|
||||
{
|
||||
public async Task<bool> PrintBadge(
|
||||
Dictionary<string, object> emp,
|
||||
Dictionary<string, object> comp,
|
||||
string ip
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var frontBitmap = GenerateFrontSide(emp, comp);
|
||||
using var backBitmap = GenerateBackSide(emp, comp);
|
||||
|
||||
string frontBase64 = ConvertBitmapToBase64(frontBitmap);
|
||||
string backBase64 = ConvertBitmapToBase64(backBitmap);
|
||||
|
||||
var (ok, error) = await PrintBadgeImages(frontBase64, backBase64, ip);
|
||||
return ok;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Print Engine Error: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private SKBitmap GenerateFrontSide(
|
||||
Dictionary<string, object> emp,
|
||||
Dictionary<string, object> comp
|
||||
)
|
||||
{
|
||||
var bitmap = new SKBitmap(648, 1016);
|
||||
using var canvas = new SKCanvas(bitmap);
|
||||
canvas.Clear(SKColors.White);
|
||||
|
||||
using var paint = new SKPaint { IsAntialias = true };
|
||||
using var font = new SKFont(SKTypeface.FromFamilyName("Arial"), 40);
|
||||
|
||||
// 1. Draw Logo
|
||||
string logoPath = comp.InternalSafeGet("logo");
|
||||
if (File.Exists(logoPath))
|
||||
{
|
||||
using var logo = SKBitmap.Decode(logoPath);
|
||||
canvas.DrawBitmap(logo, SKRect.Create(224, 40, 200, 80));
|
||||
}
|
||||
|
||||
// 2. Draw Photo
|
||||
string photoPath = emp.InternalSafeGet("ImagePath");
|
||||
if (File.Exists(photoPath))
|
||||
{
|
||||
using var photo = SKBitmap.Decode(photoPath);
|
||||
canvas.DrawBitmap(photo, SKRect.Create(149, 160, 350, 350));
|
||||
}
|
||||
|
||||
// 3. Draw First Name (Red)
|
||||
paint.Color = SKColors.Red;
|
||||
font.Size = 70;
|
||||
font.Embolden = true;
|
||||
string firstName = emp.InternalSafeGet("Data2").Split(' ')[0].ToUpper();
|
||||
canvas.DrawText(firstName, 324, 600, SKTextAlign.Center, font, paint);
|
||||
|
||||
// 4. Draw Full Name
|
||||
paint.Color = SKColors.Black;
|
||||
font.Size = 40;
|
||||
font.Embolden = false;
|
||||
canvas.DrawText(emp.InternalSafeGet("Data2"), 324, 660, SKTextAlign.Center, font, paint);
|
||||
|
||||
// 5. Draw Barcode Text
|
||||
font.Size = 80;
|
||||
canvas.DrawText(
|
||||
$"*{emp.InternalSafeGet("Data9")}*",
|
||||
324,
|
||||
850,
|
||||
SKTextAlign.Center,
|
||||
font,
|
||||
paint
|
||||
);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private SKBitmap GenerateBackSide(
|
||||
Dictionary<string, object> emp,
|
||||
Dictionary<string, object> comp
|
||||
)
|
||||
{
|
||||
var bitmap = new SKBitmap(1016, 648);
|
||||
using var canvas = new SKCanvas(bitmap);
|
||||
canvas.Clear(SKColors.White);
|
||||
|
||||
using var paint = new SKPaint { IsAntialias = true, Color = SKColors.Black };
|
||||
using var font = new SKFont(SKTypeface.FromFamilyName("Arial"), 25);
|
||||
|
||||
canvas.DrawText(
|
||||
$"This badge is the property of {comp.InternalSafeGet("Name")} L.L.C.",
|
||||
508,
|
||||
100,
|
||||
SKTextAlign.Center,
|
||||
font,
|
||||
paint
|
||||
);
|
||||
|
||||
canvas.DrawText(emp.InternalSafeGet("Data1"), 100, 200, SKTextAlign.Left, font, paint);
|
||||
canvas.DrawText(emp.InternalSafeGet("Data10"), 100, 240, SKTextAlign.Left, font, paint);
|
||||
|
||||
canvas.DrawText(comp.InternalSafeGet("Address"), 500, 200, SKTextAlign.Left, font, paint);
|
||||
canvas.DrawText(
|
||||
$"{comp.InternalSafeGet("City")}, {comp.InternalSafeGet("State")} {comp.InternalSafeGet("Zip")}",
|
||||
500,
|
||||
240,
|
||||
SKTextAlign.Left,
|
||||
font,
|
||||
paint
|
||||
);
|
||||
|
||||
font.Size = 80;
|
||||
canvas.DrawText(
|
||||
$"*{emp.InternalSafeGet("Data9")}*",
|
||||
508,
|
||||
500,
|
||||
SKTextAlign.Center,
|
||||
font,
|
||||
paint
|
||||
);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private string ConvertBitmapToBase64(SKBitmap bitmap)
|
||||
{
|
||||
using var image = SKImage.FromBitmap(bitmap);
|
||||
using var data = image.Encode(SKEncodedImageFormat.Png, 100);
|
||||
return Convert.ToBase64String(data.ToArray());
|
||||
}
|
||||
|
||||
public async Task<List<string>> DiscoverPrinters()
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
List<string> list = new();
|
||||
var handler = new InternalStatusDiscoveryHandler();
|
||||
NetworkDiscoverer.FindPrinters(handler);
|
||||
int wait = 0;
|
||||
while (!handler.IsDiscoveryComplete && wait < 5000)
|
||||
{
|
||||
Thread.Sleep(200);
|
||||
wait += 200;
|
||||
}
|
||||
foreach (var p in handler.DiscoveredPrinters)
|
||||
list.Add(p.Address);
|
||||
return list;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<(bool ok, string error)> PrintBadgeImages(
|
||||
string front,
|
||||
string back,
|
||||
string ip
|
||||
)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ip))
|
||||
return (false, "No IP");
|
||||
Connection connection = new TcpConnection(ip, TcpConnection.DEFAULT_ZPL_TCP_PORT);
|
||||
Zebra.Sdk.Card.Printer.ZebraCardPrinter? cardPrinter = null;
|
||||
try
|
||||
{
|
||||
connection.Open();
|
||||
cardPrinter = ZebraCardPrinterFactory.GetInstance(connection);
|
||||
using ZebraCardGraphics g = new ZebraCardGraphics(cardPrinter);
|
||||
List<Zebra.Sdk.Card.Containers.GraphicsInfo> info = new();
|
||||
if (!string.IsNullOrEmpty(front))
|
||||
{
|
||||
g.DrawImage(
|
||||
Convert.FromBase64String(front),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
(Zebra.Sdk.Card.Graphics.Enumerations.RotationType)0
|
||||
);
|
||||
info.Add(
|
||||
new Zebra.Sdk.Card.Containers.GraphicsInfo
|
||||
{
|
||||
Side = Zebra.Sdk.Card.Enumerations.CardSide.Front,
|
||||
PrintType = Zebra.Sdk.Card.Enumerations.PrintType.Color,
|
||||
GraphicData = g.CreateImage(),
|
||||
}
|
||||
);
|
||||
}
|
||||
cardPrinter.Print(1, info);
|
||||
return (true, "");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
cardPrinter?.Destroy();
|
||||
if (connection.Connected)
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed missing classes and helpers
|
||||
public class InternalStatusDiscoveryHandler : DiscoveryHandler
|
||||
{
|
||||
public List<DiscoveredPrinter> DiscoveredPrinters { get; } = new();
|
||||
public bool IsDiscoveryComplete { get; private set; } = false;
|
||||
|
||||
public void FoundPrinter(DiscoveredPrinter printer) => DiscoveredPrinters.Add(printer);
|
||||
|
||||
public void DiscoveryFinished() => IsDiscoveryComplete = true;
|
||||
|
||||
public void DiscoveryError(string message) => IsDiscoveryComplete = true;
|
||||
}
|
||||
|
||||
internal static class PrinterExtensions
|
||||
{
|
||||
public static string InternalSafeGet(this Dictionary<string, object> dict, string key) =>
|
||||
dict.TryGetValue(key, out var val) && val != null ? val.ToString()! : "";
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using System.Data;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace FrymasterBadgeApp.Services;
|
||||
|
||||
public class SqlService
|
||||
{
|
||||
private readonly string _connectionString;
|
||||
|
||||
public SqlService(IConfiguration configuration)
|
||||
{
|
||||
// Pulls the "Default" string from your JSON structure
|
||||
_connectionString =
|
||||
configuration.GetConnectionString("Default")
|
||||
?? throw new Exception("Connection string 'Default' not found in appsettings.json");
|
||||
}
|
||||
|
||||
public List<Dictionary<string, object>> Query(string sql, SqlParameter[]? parameters = null)
|
||||
{
|
||||
var rows = new List<Dictionary<string, object>>();
|
||||
using var conn = new SqlConnection(_connectionString);
|
||||
using var cmd = new SqlCommand(sql, conn);
|
||||
if (parameters != null)
|
||||
cmd.Parameters.AddRange(parameters);
|
||||
|
||||
conn.Open();
|
||||
using var reader = cmd.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
var row = new Dictionary<string, object>();
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
row[reader.GetName(i)] = reader.GetValue(i);
|
||||
}
|
||||
rows.Add(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
public void Execute(string sql, SqlParameter[]? parameters = null)
|
||||
{
|
||||
using var conn = new SqlConnection(_connectionString);
|
||||
using var cmd = new SqlCommand(sql, conn);
|
||||
if (parameters != null)
|
||||
cmd.Parameters.AddRange(parameters);
|
||||
conn.Open();
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,23 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Badge Maker</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Libre+Barcode+128&display=swap" rel="stylesheet">
|
||||
<script type="module" crossorigin src="/assets/index-CNw6GJoD.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DnBq_fk4.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="_framework/blazor.webview.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
Loading…
Reference in New Issue