Skip to content

Commit b3427ad

Browse files
authored
Merge pull request #76 from SensitTechnologies/phoebe-product-association-dialog
Phoebe Improvements
2 parents 6210fbb + ca0fcb1 commit b3427ad

26 files changed

+1373
-945
lines changed

MESS/MESS.Blazor/Components/Pages/Phoebe/MenuBar/MenuBarPhoebe.razor

Lines changed: 87 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
<div class="menu-left-buttons">
1919
<button class="btn-small" @onclick="ToggleFileMenu" id="btnOpen1" aria-expanded="@open">File</button>
2020
<FluentMenu Anchor="btnOpen1" Open="@open" OpenChanged="@(val => open = val)" OnMenuChange="OnMenuChange" VerticalThreshold="170">
21-
<FluentMenuItem OnClick="@(() => TriggerAndClose(OnNew))">New</FluentMenuItem>
21+
<FluentMenuItem OnClick="@ShowNewInstructionDialog">New</FluentMenuItem>
22+
<FluentDivider Style="width: 100%;" Orientation=Orientation.Horizontal></FluentDivider>
2223
<FluentMenuItem OnClick="@(() => TriggerImport())">Import</FluentMenuItem>
2324
<FluentMenuItem OnClick="@(() => TriggerExport())" Disabled="@(!HasInstruction)">Export</FluentMenuItem>
25+
<FluentDivider Style="width: 100%;" Orientation=Orientation.Horizontal></FluentDivider>
2426
<FluentMenuItem OnClick="@(() => TriggerAndClose(OnSave))" Disabled="@(!IsDirty)">Save</FluentMenuItem>
25-
<FluentMenuItem OnClick="@(() => LogAndClose("Save As"))">Save As</FluentMenuItem>
26-
<FluentMenuItem OnClick="@(() => TriggerAndClose(OnRevertChanges))" Disabled="@(!IsDirty)">Revert Changes</FluentMenuItem>
27+
<FluentMenuItem OnClick="@ShowSaveAsDialog" Disabled="@(!HasInstruction)">Save As</FluentMenuItem>
2728
<AuthorizeView Roles="Administrator">
29+
<FluentDivider Style="width: 100%;" Orientation=Orientation.Horizontal></FluentDivider>
2830
<FluentMenuItem OnClick="@(() => TriggerAndClose(OnDelete))">Delete</FluentMenuItem>
2931
</AuthorizeView>
3032
</FluentMenu>
@@ -38,7 +40,7 @@
3840
</FluentMenu>
3941
</div>
4042

41-
<div class="inputs-container">
43+
<div class="inputs-container title-version-container">
4244
<input
4345
type="text"
4446
class="menu-input"
@@ -70,24 +72,33 @@
7072
</div>
7173

7274
<div class="menu-right-content">
73-
<button class="btn-small" title="Version History" @onclick="ToggleVersionHistorySidebar">⏲</button>
75+
<button class="btn-small" title="Version History" @onclick="ToggleVersionHistorySidebar">
76+
<i class="bi bi-clock-history"></i>
77+
</button>
7478
<div class="save-button-container">
7579
<button class="btn-small" title="Save" @onclick="HandleSave" disabled="@(!IsDirty)">
76-
🖫
80+
<i class="bi bi-floppy"></i>
7781
@if (IsDirty)
7882
{
7983
<span class="red-dot"></span>
8084
}
8185
</button>
8286
</div>
83-
<button class="btn-small dark-toggle-btn" title="Dark Mode" @onclick="ToggleDarkMode">@darkModeIcon</button>
87+
88+
<button class="btn-small dark-toggle-btn" title="Dark Mode" @onclick="ToggleDarkMode">
89+
@((darkModeIcon == "bi bi-moon-stars") ?
90+
(MarkupString)"<i class=\"bi bi-moon-stars\"></i>" :
91+
(MarkupString)"<i class=\"bi bi-sun\"></i>")
92+
</button>
8493

8594
@if (!string.IsNullOrWhiteSpace(ActiveLineOperator))
8695
{
8796
<div class="logout-container">
8897
<span>@ActiveLineOperator</span>
8998
<form method="post" action="/api/auth/logout" class="logout-form">
90-
<button type="submit" class="btn-small" title="Logout">Logout</button>
99+
<button type="submit" class="btn-small" title="Logout">
100+
Logout <i class="bi bi-box-arrow-right ms-1"></i>
101+
</button>
91102
</form>
92103
</div>
93104
}
@@ -114,6 +125,16 @@
114125
Visible="@showExportDialog"
115126
VisibleChanged="@(val => showExportDialog = val)" />
116127

128+
<ProductAssociationDialog
129+
AllProducts="@Products"
130+
SelectedProducts="@DialogSelectedProducts"
131+
SelectedProductsChanged="OnDialogSelectedProductsChanged"
132+
IsVisible="@isAnyDialogVisible"
133+
IsVisibleChanged="@(val => isAnyDialogVisible = val)"
134+
HeaderText="@dialogHeaderText"
135+
ConfirmButtonText="@confirmButtonText"
136+
OnConfirmed="@OnDialogConfirmed" />
137+
117138
@code {
118139
private string? ActiveLineOperator { get; set; }
119140

@@ -125,28 +146,29 @@
125146
public EventCallback OnSidebarToggle { get; set; }
126147

127148
/// <summary>
128-
/// Event callback triggered when the user selects "New" to create a new work instruction.
149+
/// Event callback triggered when the user selects "New" to create a new work instruction. This passes up a work
150+
/// instruction title and list of associated products.
129151
/// </summary>
130-
[Parameter] public EventCallback OnNew { get; set; }
152+
[Parameter] public EventCallback<(string Title, List<Product> Products)> OnNewConfirmed { get; set; }
131153

132154
/// <summary>
133155
/// Event callback triggered when the user selects "Save" to persist current edits to the work instruction either
134156
/// via versioning or direct edits.
135157
/// </summary>
136158
[Parameter]
137159
public EventCallback OnSave { get; set; }
160+
161+
/// <summary>
162+
/// Callback invoked when the user confirms the "Save As" action from the dialog.
163+
/// The associated products and work instruction title are passed up.
164+
/// </summary>
165+
[Parameter] public EventCallback<(string Title, List<Product> Products)> OnSaveAsConfirmed { get; set; }
138166

139167
/// <summary>
140168
/// Event callback triggered when the user selects "Delete" to completely remove a work instruction.
141169
/// </summary>
142170
[Parameter]
143171
public EventCallback OnDelete { get; set; }
144-
145-
/// <summary>
146-
/// Event callback triggered when the user selects "Revert Changes" to discard unsaved edits and restore the last saved state.
147-
/// </summary>
148-
[Parameter]
149-
public EventCallback OnRevertChanges { get; set; }
150172

151173
/// <summary>
152174
/// Event callback triggered when the user restores a previous version from the version history sidebar.
@@ -162,7 +184,7 @@
162184

163185
/// <summary>
164186
/// Indicates whether there are unsaved changes in the current work instruction editor session.
165-
/// Used to enable or disable menu actions like Save or Revert Changes.
187+
/// Used to enable or disable menu actions like Save.
166188
/// </summary>
167189
[Parameter] public bool IsDirty { get; set; }
168190

@@ -198,15 +220,21 @@
198220

199221
private bool open = false;
200222
private bool openOptions = false;
201-
private string darkModeIcon = "🌙";
223+
private string darkModeIcon = "bi bi-moon-stars";
202224
private bool HasInstruction => CurrentInstruction != null;
203225

204226
private List<WorkInstruction> _versionHistoryList = new();
205227
private bool _isLoadingVersions = false;
206228

207229
private bool showImportDialog;
208-
209230
private bool showExportDialog = false;
231+
232+
private bool isAnyDialogVisible;
233+
private string dialogHeaderText = "";
234+
private string confirmButtonText = "";
235+
private EventCallback<(string Title, List<Product> Products)> OnDialogConfirmed;
236+
237+
private List<Product> DialogSelectedProducts { get; set; } = new List<Product>();
210238

211239
/// <summary>
212240
/// The list of products to show in the dropdown.
@@ -282,7 +310,7 @@
282310
{
283311
await JS.InvokeVoidAsync("document.body.classList.toggle", "dark-mode");
284312
var isDark = await JS.InvokeAsync<bool>("document.body.classList.contains", "dark-mode");
285-
darkModeIcon = isDark ? "☀️" : "🌙";
313+
darkModeIcon = isDark ? "bi bi-sun" : "bi bi-moon-stars";
286314
}
287315

288316
private async Task ToggleSidebar()
@@ -427,4 +455,43 @@
427455
{
428456
showImportDialog = isOpen;
429457
}
458+
459+
private void ShowNewInstructionDialog()
460+
{
461+
isAnyDialogVisible = true;
462+
dialogHeaderText = "Create New Work Instruction";
463+
confirmButtonText = "Open New";
464+
OnDialogConfirmed = EventCallback.Factory.Create<(string, List<Product>)>(this, HandleNewDialogConfirmed);
465+
}
466+
467+
private void ShowSaveAsDialog()
468+
{
469+
isAnyDialogVisible = true;
470+
DialogSelectedProducts = CurrentInstruction?.Products?.ToList() ?? new();
471+
dialogHeaderText = "Save Work Instruction As";
472+
confirmButtonText = "Save New";
473+
OnDialogConfirmed = EventCallback.Factory.Create<(string, List<Product>)>(this, HandleSaveAsDialogConfirmed);
474+
}
475+
476+
private async Task HandleNewDialogConfirmed((string Title, List<Product> Products) data)
477+
{
478+
Log.Information("[MenuBarPhoebe] HandleNewDialogConfirmed called: {DataTitle}, {ProductsCount} products", data.Title, data.Products.Count);
479+
isAnyDialogVisible = false;
480+
if (OnNewConfirmed.HasDelegate)
481+
await OnNewConfirmed.InvokeAsync(data);
482+
}
483+
484+
private async Task HandleSaveAsDialogConfirmed((string Title, List<Product> Products) data)
485+
{
486+
Log.Information("[MenuBarPhoebe] HandleSaveAsDialogConfirmed called: {DataTitle}, {ProductsCount} products", data.Title, data.Products.Count);
487+
isAnyDialogVisible = false;
488+
if (OnSaveAsConfirmed.HasDelegate)
489+
await OnSaveAsConfirmed.InvokeAsync(data);
490+
}
491+
492+
private Task OnDialogSelectedProductsChanged(List<Product> products)
493+
{
494+
DialogSelectedProducts = products;
495+
return Task.CompletedTask;
496+
}
430497
}

MESS/MESS.Blazor/Components/Pages/Phoebe/MenuBar/MenuBarPhoebe.razor.css

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
--menu-border: #ffd502;
88
--menu-item-color: #212529;
99
--menu-item-hover-bg: #fff8cc; /* unused on hover now */
10+
--input-disabled-bg: rgba(240, 240, 240, 0.85); /* Light neutral gray */
11+
--input-disabled-color: #555;
1012
}
1113

1214
body.dark-mode {
@@ -15,9 +17,9 @@ body.dark-mode {
1517
--btn-hover-bg: #ffdf33;
1618

1719
--menu-bg: #1a1a1a;
18-
--menu-border: #4a4300;
1920
--menu-item-color: #ffffff;
20-
--menu-item-hover-bg: #333300; /* unused on hover now */
21+
--input-disabled-bg: rgba(50, 50, 50, 0.8); /* Darker gray */
22+
--input-disabled-color: #aaa;
2123
}
2224

2325
/* === Menu Bar Layout === */
@@ -121,7 +123,6 @@ body.dark-mode .menu-bar-card {
121123
font-size: 0.9rem;
122124
border: none;
123125
border-radius: 0.25rem;
124-
background-color: rgba(255, 255, 255, 0.8);
125126
min-width: 120px;
126127
transition: background-color 0.2s ease, box-shadow 0.2s ease;
127128
color: black;
@@ -138,18 +139,43 @@ body.dark-mode .menu-bar-card {
138139
.menu-input:focus,
139140
.menu-input-secondary:focus {
140141
outline: none;
141-
box-shadow: 0 0 0 0.1rem rgba(255, 215, 0, 0.5);
142142
}
143143

144-
body.dark-mode .menu-input,
145-
body.dark-mode .menu-input-secondary {
146-
background-color: rgba(44, 44, 44, 0.7);
147-
color: white;
144+
/* Default: only apply a neutral gray when disabled */
145+
.menu-input:disabled,
146+
.menu-input-secondary:disabled {
147+
background-color: var(--input-disabled-bg) !important;
148+
color: var(--input-disabled-color) !important;
148149
}
149150

150-
body.dark-mode .menu-input::placeholder,
151-
body.dark-mode .menu-input-secondary::placeholder {
152-
color: #aaa;
151+
/* Enabled inputs: almost white, with hover/focus transition */
152+
.menu-input:enabled,
153+
.menu-input-secondary:enabled {
154+
background-color: #e8e8e8; /* light gray */
155+
transition: background-color 0.2s ease, box-shadow 0.2s ease;
156+
color: black;
157+
}
158+
159+
.menu-input:enabled:hover,
160+
.menu-input:enabled:focus,
161+
.menu-input-secondary:enabled:hover,
162+
.menu-input-secondary:enabled:focus {
163+
background-color: #f0f0f0; /* slightly lighter gray on hover/focus */
164+
outline: none;
165+
}
166+
167+
/* Dark mode support */
168+
body.dark-mode .menu-input:enabled,
169+
body.dark-mode .menu-input-secondary:enabled {
170+
background-color: #2c2c2c;
171+
color: #fff;
172+
}
173+
174+
body.dark-mode .menu-input:enabled:hover,
175+
body.dark-mode .menu-input:enabled:focus,
176+
body.dark-mode .menu-input-secondary:enabled:hover,
177+
body.dark-mode .menu-input-secondary:enabled:focus {
178+
background-color: #3a3a3a;
153179
}
154180

155181
/* === Fluent Menu === */
@@ -178,28 +204,40 @@ fluent-menu-item:hover::part(control) {
178204
color: var(--menu-item-color) !important;
179205
}
180206

207+
/* Dark mode fluent menu overrides */
181208
body.dark-mode fluent-menu::part(control),
182209
body.dark-mode fluent-menu-item::part(control) {
183210
background-color: var(--menu-bg) !important;
184-
color: #fff !important;
211+
color: var(--menu-item-color) !important;
185212
}
186213

187214
body.dark-mode fluent-menu-item:hover::part(control) {
188215
background-color: #ffdf33 !important;
189-
color: #fff !important;
216+
color: black !important;
190217
}
191218

192219
.save-button-container {
193220
position: relative;
194221
display: inline-block;
195222
}
196223

224+
.save-button-container button {
225+
position: relative; /* so the icon is the positioning context */
226+
}
227+
197228
.red-dot {
198229
position: absolute;
199-
top: 2px;
200-
right: 2px;
201-
height: 8px;
202-
width: 8px;
230+
bottom: 6px;
231+
right: 11px;
232+
height: 6px;
233+
width: 6px;
203234
background-color: red;
204235
border-radius: 50%;
236+
border: 1px solid transparent;
237+
z-index: 100;
238+
pointer-events: none;
205239
}
240+
241+
242+
243+

0 commit comments

Comments
 (0)