05/29 Update functionalities
This commit is contained in:
@@ -1877,10 +1877,13 @@ struct SettingsView: View {
|
||||
Section {
|
||||
Button(role: .destructive) {
|
||||
Task {
|
||||
// Clear server-pulled issues before logging out so stale
|
||||
// records from a previous server/domain don't persist into
|
||||
// the next session. Device-created pending issues are preserved.
|
||||
clearServerPulledData()
|
||||
// Do NOT clear server-pulled data on a plain logout —
|
||||
// the user is logging out of the same server, so cached
|
||||
// facilities, issues, and templates are still valid on
|
||||
// their next login. Clearing here leaves the issues list
|
||||
// empty until a full sync succeeds, which breaks offline use.
|
||||
// Server-pulled data is only cleared when switching servers
|
||||
// (see the Switch & Log Out alert below).
|
||||
sync.resetNotificationPoller()
|
||||
await auth.logout()
|
||||
}
|
||||
|
||||
@@ -488,9 +488,13 @@ struct GridFormView: View {
|
||||
"table": 80,
|
||||
]
|
||||
|
||||
// Width captured via PreferenceKey — updates on rotation & split-screen.
|
||||
// Default 952 = 1000 max-width − 2×24 outer padding (safe iPad landscape floor).
|
||||
@State private var containerWidth: CGFloat = 952
|
||||
// Width captured via PreferenceKey — updates on rotation, split-screen,
|
||||
// and sheet presentation.
|
||||
// 0 = unmeasured; the grid does NOT render fields until a real width arrives.
|
||||
// This prevents the first-frame overflow that occurred when a sheet modal
|
||||
// (narrower than full screen on iPad 10th gen) was rendered with the old
|
||||
// hardcoded 952 pt fallback, causing fields to overflow the modal bounds.
|
||||
@State private var containerWidth: CGFloat = 0
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .topLeading) {
|
||||
@@ -514,15 +518,21 @@ struct GridFormView: View {
|
||||
}
|
||||
)
|
||||
|
||||
// ── Field overlays ─────────────────────────────────────────────
|
||||
let cellW = computedCellW
|
||||
let cellH = cellW * Self.cellAspect
|
||||
// ── Field overlays — only rendered after width is measured ─────
|
||||
// containerWidth == 0 means the PreferenceKey has not fired yet
|
||||
// (first layout pass). Skipping the overlay pass on the zero frame
|
||||
// prevents fields from being positioned using a stale width and
|
||||
// overflowing the modal on narrow sheet presentations (iPad 10th gen).
|
||||
if containerWidth > 0 {
|
||||
let cellW = computedCellW
|
||||
let cellH = cellW * Self.cellAspect
|
||||
|
||||
ForEach(schema.indices, id: \.self) { idx in
|
||||
let field = schema[idx]
|
||||
let ftype = field["type"] as? String ?? "text"
|
||||
if !["button_submit", "button_print", "button_email"].contains(ftype) {
|
||||
gridCell(field: field, cellW: cellW, cellH: cellH)
|
||||
ForEach(schema.indices, id: \.self) { idx in
|
||||
let field = schema[idx]
|
||||
let ftype = field["type"] as? String ?? "text"
|
||||
if !["button_submit", "button_print", "button_email"].contains(ftype) {
|
||||
gridCell(field: field, cellW: cellW, cellH: cellH)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,7 +541,10 @@ struct GridFormView: View {
|
||||
}
|
||||
// Height is always derived from the same arithmetic as cell offsets —
|
||||
// the ScrollView measures this frame and can never be wrong.
|
||||
.frame(height: canvasHeight() + 2 * Self.cardPadding)
|
||||
// When containerWidth is 0, canvasHeight() still returns the correct
|
||||
// value (it uses computedCellW which returns 0 when containerWidth is 0),
|
||||
// so the card reserves space and avoids a layout jump.
|
||||
.frame(height: containerWidth > 0 ? canvasHeight() + 2 * Self.cardPadding : 0)
|
||||
}
|
||||
|
||||
// ── Derived cell width from current containerWidth ────────────────────
|
||||
@@ -992,26 +1005,31 @@ struct CellPassFail: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
// Each button expands equally to fill the available cell width.
|
||||
// .lineLimit(1) + fixedSize prevents multi-line wrapping when
|
||||
// custom option labels are long or the cell is narrow.
|
||||
HStack(spacing: 6) {
|
||||
ForEach(options, id: \.self) { opt in
|
||||
let isPass = isPassOption(opt)
|
||||
let isActive = value == opt
|
||||
let baseColor: Color = isPass ? .green : .red
|
||||
let isPass = isPassOption(opt)
|
||||
let isActive = value == opt
|
||||
let color: Color = isPass ? .green : .red
|
||||
Button {
|
||||
value = isActive ? "" : opt
|
||||
} label: {
|
||||
Text(opt)
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
.padding(.horizontal, 14)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.75)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 6)
|
||||
.background(isActive ? baseColor : Color.clear)
|
||||
.foregroundStyle(isActive ? .white : baseColor)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(isActive ? color : Color.clear)
|
||||
.foregroundStyle(isActive ? .white : color)
|
||||
.clipShape(Capsule())
|
||||
.overlay(Capsule().stroke(baseColor, lineWidth: 2))
|
||||
.overlay(Capsule().stroke(color, lineWidth: 2))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user