Feature/ab#32632 worksheet merge dev#2345
Conversation
Feature/ab#32632 worksheets
There was a problem hiding this comment.
Pull request overview
This PR extends the Unity Flex worksheet configuration and rendering pipeline by introducing worksheet archiving, section-level layout metadata (field width), and richer custom-field presentation options (label positioning, placeholders, etc.), with accompanying UI updates across the worksheet configuration screens and widgets.
Changes:
- Added worksheet archiving support (DB column + UI actions) and archived-aware UI behavior.
- Introduced
WorksheetSection.Definition(jsonb) to store section layout metadata (e.g., field width) and render it in worksheet instances. - Refactored worksheet instance rendering into a shared partial and expanded custom-field display attributes (label position, hide label, disabled, placeholder, styles).
Reviewed changes
Copilot reviewed 42 out of 44 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/GrantTenantDbContextModelSnapshot.cs | Updates EF snapshot for worksheet archiving + section definition jsonb. |
| applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260414100000_AddIsArchivedToWorksheets.cs | Adds IsArchived column to Flex.Worksheets. |
| applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260413153147_AddDefinitionToWorksheetSections.cs | Adds Definition jsonb column to Flex.WorksheetSections. |
| applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260413153147_AddDefinitionToWorksheetSections.Designer.cs | Generated migration designer snapshot reflecting schema changes. |
| applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/fluentui-icons.css | Adds a folder icon class used for archive UI affordances. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetWidget/WorksheetWidget.cs | Ensures new modal JS is included in the widget bundle. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetWidget/Worksheet.js | Adds archive/unarchive behavior and adjusts modal configuration. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetWidget/Worksheet.css | Adds styling for section collapse toggles in worksheet configuration UI. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetWidget/Default.cshtml | Adds archive/unarchive buttons and disables editing actions for archived worksheets. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetListWidget/WorksheetListWidget.cs | Adds WorksheetInstance widget styles to support preview rendering. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetListWidget/WorksheetList.js | Updates worksheet list interactions, sorting behavior, and preview refresh handling. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetListWidget/WorksheetList.css | Adds visual styling for archived worksheets in the accordion list. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetListWidget/Default.cshtml | Marks archived worksheets in the list and exposes data-is-archived to JS filters. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetInstanceWidget/_WorksheetSections.cshtml | New shared partial for section + field rendering (collapse, label position, width, etc.). |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetInstanceWidget/WorksheetInstanceWidget.cs | Maps section FieldWidth into the instance view model. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetInstanceWidget/ViewModels/WorksheetSectionViewModel.cs | Adds FieldWidth to section view model. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetInstanceWidget/ViewModels/WorksheetSectionRenderModel.cs | New render model for the shared sections partial. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetInstanceWidget/Default.css | Adds CSS for label-left vs label-top layouts and collapsible section headers. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/WorksheetInstanceWidget/Default.cshtml | Refactors rendering to use _WorksheetSections partial for single/multi worksheet cases. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/TextAreaWidget/Default.cshtml | Reuses parsed definition and adds placeholder support. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/DefaultFieldWidget/DefaultFieldWidget.cs | New default widget for rendering non-specialized custom-field types. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/DefaultFieldWidget/DefaultFieldViewModel.cs | View model for the default field widget. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/DefaultFieldWidget/Default.cshtml | Renders a generic input and supports placeholder/min/max/etc. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/CurrencyWidget/Default.cshtml | Adds placeholder support for currency input. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Pages/WorksheetConfiguration/UpsertSectionModal.cshtml.cs | Adds FieldWidth binding/validation and persists width into section definition. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Pages/WorksheetConfiguration/UpsertSectionModal.cshtml | Adds slider/number controls to configure per-section field width. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Pages/WorksheetConfiguration/UpsertCustomFieldModal.js | New modal script for tabbed UI, hints, and validation badge behavior. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Pages/WorksheetConfiguration/UpsertCustomFieldModal.cshtml.cs | Adds custom-field display/security options and merges them into definitions. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Pages/WorksheetConfiguration/UpsertCustomFieldModal.cshtml | Refactors modal into Display/Attributes tabs and adds new form controls. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Pages/WorksheetConfiguration/Index.js | Adds column resizer and client-side filters (published/unpublished/archived). |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Pages/WorksheetConfiguration/Index.css | Adds layout styles for resizer, filters, and custom-field modal tab UI. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Pages/WorksheetConfiguration/Index.cshtml | Updates worksheet configuration layout (sticky header, filters, resizable columns). |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Worksheets/Definitions/CustomFieldDefinition.cs | Extends field definition schema with UI behavior (hidden/disabled/label/placeholder/etc.). |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Localization/Flex/en.json | Shortens several worksheet configuration button labels. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application/Worksheets/WorksheetSectionAppService.cs | Persists section field width into Definition JSON. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application/Worksheets/WorksheetAppService.cs | Adds Archive API to toggle worksheet archived state. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application/FlexApplicationAutoMapperProfile.cs | Parses fieldWidth from section definition JSON into DTO. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application/Domain/Worksheets/WorksheetSection.cs | Adds Definition jsonb storage to worksheet sections. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application/Domain/Worksheets/Worksheet.cs | Adds IsArchived state + setter and extends section update to include definition. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application.Contracts/Worksheets/WorksheetSectionDto.cs | Adds FieldWidth and Definition to section DTO. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application.Contracts/Worksheets/WorksheetDto.cs | Adds IsArchived to worksheet DTO. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application.Contracts/Worksheets/IWorksheetAppService.cs | Exposes Archive API contract. |
| applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application.Contracts/Worksheets/EditSectionDto.cs | Adds FieldWidth to section edit DTO. |
| function makeSectionsAndFieldsSortable() { | ||
| makeCustomFieldsSortable(); | ||
| makeSectionsSortable(); | ||
| } |
There was a problem hiding this comment.
This file now defines makeSectionsAndFieldsSortable/makeCustomFieldsSortable/makeSectionsSortable (and related helpers) twice: once inside the $(function(){...}) closure and again as global functions below. Because the closure-scoped versions are the ones being called (they shadow the globals), the new archived-aware disabled: isArchived logic added in the global versions won’t take effect, and archived worksheets will still be sortable. Remove the duplicate set, or update the existing closure-scoped implementations to include the archived check and keep only one source of truth.
| _ = new Sortable(div, { | ||
| animation: 150, | ||
| disabled: isArchived, | ||
| onEnd: function (evt) { |
There was a problem hiding this comment.
_ = new Sortable(...) assigns to an undeclared identifier, creating an implicit global (or throwing in strict mode) and potentially clobbering other globals (commonly _). Use const sortable = new Sortable(...) (or just new Sortable(...) if you don’t need the instance) to avoid leaking globals.
| var fieldStyleAttr = !string.IsNullOrEmpty(fieldDef?.Style) ? $" style=\"{fieldDef.Style}\"" : ""; | ||
| var fieldExtraCssClass = !string.IsNullOrEmpty(fieldDef?.CssClass) ? $" {fieldDef.CssClass}" : ""; | ||
| var labelStyleAttr = !string.IsNullOrEmpty(fieldDef?.LabelStyle) ? $" style=\"{fieldDef.LabelStyle}\"" : ""; | ||
|
|
||
| <div class="worksheet_field worksheet_field_@Model.UiAnchor @labelPositionClass@fieldExtraCssClass" | ||
| @Html.Raw(fieldDef?.IsHidden == true ? "hidden" : "") | ||
| @Html.Raw(fieldWidthStyle) | ||
| @Html.Raw(fieldStyleAttr)> |
There was a problem hiding this comment.
fieldDef.Style is injected into the DOM via @Html.Raw(fieldStyleAttr) where fieldStyleAttr is built from persisted JSON. This bypasses Razor’s output encoding and allows attribute-breaking payloads (e.g., quotes) which can become XSS. Prefer binding as an encoded attribute value (e.g., style="@fieldDef.Style") and/or validate/sanitize allowed CSS before persisting; avoid Html.Raw for user-controlled strings. The same concern applies to labelStyleAttr on the <label>.
| var existingDef = JsonSerializer.Deserialize<CustomFieldDefinition>(customField.Definition); | ||
| IsHidden = existingDef?.IsHidden ?? false; | ||
| HideLabel = existingDef?.HideLabel ?? false; | ||
| IsDisabled = existingDef?.IsDisabled ?? false; | ||
| LabelPosition = existingDef?.LabelPosition ?? "Top"; | ||
| LabelStyle = existingDef?.LabelStyle; | ||
| LabelCssClass = existingDef?.LabelCssClass; | ||
| SecurityClassification = existingDef?.SecurityClassification; | ||
| Placeholder = existingDef?.Placeholder; |
There was a problem hiding this comment.
JsonSerializer.Deserialize<CustomFieldDefinition>(customField.Definition) can throw if the stored JSON is malformed, which would fail the modal GET with a 500. Consider wrapping this in a JsonException catch (and defaulting the new UI fields), or reuse the existing ConvertDefinition(...) helper which already handles JsonException by returning null.
| var existingDef = JsonSerializer.Deserialize<CustomFieldDefinition>(customField.Definition); | |
| IsHidden = existingDef?.IsHidden ?? false; | |
| HideLabel = existingDef?.HideLabel ?? false; | |
| IsDisabled = existingDef?.IsDisabled ?? false; | |
| LabelPosition = existingDef?.LabelPosition ?? "Top"; | |
| LabelStyle = existingDef?.LabelStyle; | |
| LabelCssClass = existingDef?.LabelCssClass; | |
| SecurityClassification = existingDef?.SecurityClassification; | |
| Placeholder = existingDef?.Placeholder; | |
| try | |
| { | |
| var existingDef = JsonSerializer.Deserialize<CustomFieldDefinition>(customField.Definition); | |
| IsHidden = existingDef?.IsHidden ?? false; | |
| HideLabel = existingDef?.HideLabel ?? false; | |
| IsDisabled = existingDef?.IsDisabled ?? false; | |
| LabelPosition = existingDef?.LabelPosition ?? "Top"; | |
| LabelStyle = existingDef?.LabelStyle; | |
| LabelCssClass = existingDef?.LabelCssClass; | |
| SecurityClassification = existingDef?.SecurityClassification; | |
| Placeholder = existingDef?.Placeholder; | |
| } | |
| catch (JsonException) | |
| { | |
| // Preserve the existing/default UI field values when stored JSON is malformed. | |
| } |
| <button data-worksheet-id="@worksheet.Id" data-is-archived="true" class="btn btn-light archive-worksheet-btn worksheet-btn" type="button"> | ||
| <i class="fl fl-open-folder"></i> <span>Unarchive</span> | ||
| </button> |
There was a problem hiding this comment.
The button text "Unarchive" is hard-coded instead of localized like the other worksheet action buttons in this view. Please add a localization key (e.g., Worksheet:Configuration:UnarchiveWorksheetButtonText) and use @L[...] here for consistency and to support non-English locales.
| <button data-worksheet-id="@worksheet.Id" data-is-archived="false" class="btn btn-light archive-worksheet-btn worksheet-btn" type="button"> | ||
| <i class="fl fl-folder"></i> <span>Archive</span> | ||
| </button> |
There was a problem hiding this comment.
The button text "Archive" is hard-coded instead of localized like the other worksheet action buttons in this view. Please add a localization key (e.g., Worksheet:Configuration:ArchiveWorksheetButtonText) and use @L[...] here for consistency and to support non-English locales.
| if (doc.RootElement.TryGetProperty("fieldWidth", out var prop) && prop.TryGetInt32(out var value)) | ||
| return value > 0 ? value : null; | ||
| } | ||
| catch { /* malformed JSON — fall through */ } |
There was a problem hiding this comment.
ParseFieldWidth uses a blanket catch {} which will swallow non-JSON failures (e.g., OOM, cancellation) and makes troubleshooting harder. Consider narrowing this to catch (JsonException) (and optionally FormatException) so unexpected failures still surface while still treating malformed JSON as “no fieldWidth”.
| catch { /* malformed JSON — fall through */ } | |
| catch (JsonException) { /* malformed JSON — fall through */ } |
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
|




No description provided.