Skip to content

Commit 6b745f0

Browse files
committed
Add light/dark mode, Jamf Pro JSON schema, and clean up config profile
Rework the config profile and UI theming system for AdminRightsManager. Config Profile & Deployment: - Strip all XML comments from com.adminrights.manager.mobileconfig for a clean, production-ready profile - Add com.adminrights.manager.json — a Jamf Pro JSON Schema file that can be uploaded under Application & Custom Settings to generate a GUI form for all configurable keys - Consolidate the three color keys (PrimaryColorHex, SecondaryColorHex, DarkColorHex) into a single AccentColorHex key Light/Dark Mode: - App now follows macOS system appearance automatically — no config key needed - Rewrite BrandColors.swift with an adaptive(light:dark:) factory that creates NSAppearance-aware colors - All views (NagView, ReportView, ContentView) updated to use semantic color tokens (.textPrimary, .textSecondary, .cardBackground, .accent, etc.) instead of hardcoded white/opacity values - One accent color from the config profile drives all derived colors in both modes Housekeeping: - AppConfiguration.swift reads AccentColorHex with backward-compatible fallback to legacy PrimaryColorHex - Fix 4 inconsistent default values (google.com placeholders → proper example.edu defaults) - Update README.md and CONTRIBUTING.md to reflect the new single-color approach
1 parent 3dc7ffa commit 6b745f0

10 files changed

Lines changed: 380 additions & 272 deletions

File tree

AdminRightsManager/ContentView.swift

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct ContentView: View {
1313

1414
var body: some View {
1515
ZStack {
16-
// Background gradient — brand colors from config profile
16+
// Background gradient — adapts to light/dark appearance
1717
LinearGradient(
1818
gradient: Gradient(colors: [
1919
.backgroundTop,
@@ -77,16 +77,16 @@ struct RemediatingView: View {
7777
VStack(spacing: 24) {
7878
ProgressView()
7979
.scaleEffect(2)
80-
.tint(.white)
80+
.tint(.accent)
8181

8282
Text("Removing Admin Privileges...")
8383
.font(.title2)
8484
.fontWeight(.semibold)
85-
.foregroundColor(.white)
85+
.foregroundColor(.textPrimary)
8686

8787
Text("Please wait while your account is updated.\nThis should only take a moment.")
8888
.font(.body)
89-
.foregroundColor(.white.opacity(0.7))
89+
.foregroundColor(.textSecondary)
9090
.multilineTextAlignment(.center)
9191
}
9292
.padding(40)
@@ -127,11 +127,11 @@ struct RemediationCompleteView: View {
127127
Text("Remediation Complete")
128128
.font(.title)
129129
.fontWeight(.bold)
130-
.foregroundColor(.white)
130+
.foregroundColor(.textPrimary)
131131

132132
Text("Your admin privileges have been removed. Your account is now a standard user and compliant with \(config.organizationName) policy.")
133133
.font(.body)
134-
.foregroundColor(.white.opacity(0.8))
134+
.foregroundColor(.textSecondary)
135135
.multilineTextAlignment(.center)
136136
.frame(maxWidth: 500)
137137

@@ -140,10 +140,10 @@ struct RemediationCompleteView: View {
140140
// Receipt header
141141
HStack {
142142
Image(systemName: "doc.text.fill")
143-
.foregroundColor(.brandPrimary)
143+
.foregroundColor(.accent)
144144
Text("Remediation Receipt")
145145
.font(.headline)
146-
.foregroundColor(.white)
146+
.foregroundColor(.textPrimary)
147147
Spacer()
148148
Button {
149149
copyReceipt()
@@ -153,54 +153,54 @@ struct RemediationCompleteView: View {
153153
Text(copied ? "Copied" : "Copy")
154154
}
155155
.font(.caption)
156-
.foregroundColor(.white.opacity(0.7))
156+
.foregroundColor(.textSecondary)
157157
.padding(.horizontal, 10)
158158
.padding(.vertical, 5)
159159
.background(
160160
RoundedRectangle(cornerRadius: 6)
161-
.fill(Color.white.opacity(0.1))
161+
.fill(Color.secondaryButtonBackground)
162162
)
163163
}
164164
.buttonStyle(.plain)
165165
}
166166
.padding(.horizontal, 16)
167167
.padding(.vertical, 12)
168-
.background(Color.white.opacity(0.05))
168+
.background(Color.cardBackground.opacity(0.6))
169169

170-
Divider().background(Color.white.opacity(0.1))
170+
Divider().background(Color.divider)
171171

172172
// Receipt body
173173
VStack(alignment: .leading, spacing: 8) {
174174
receiptRow(label: "Action", value: "Admin rights removed")
175175
receiptRow(label: "Status", value: "Completed successfully", valueColor: .green)
176-
Divider().background(Color.white.opacity(0.06)).padding(.vertical, 4)
176+
Divider().background(Color.divider).padding(.vertical, 4)
177177
receiptRow(label: "Date & Time", value: receipt.formattedTimestamp)
178178
receiptRow(label: "Username", value: receipt.username)
179179
receiptRow(label: "Full Name", value: receipt.fullName)
180-
Divider().background(Color.white.opacity(0.06)).padding(.vertical, 4)
180+
Divider().background(Color.divider).padding(.vertical, 4)
181181
receiptRow(label: "Computer", value: receipt.hostname)
182182
receiptRow(label: "Serial Number", value: receipt.serialNumber)
183183
receiptRow(label: "Model", value: receipt.hardwareModel)
184184
receiptRow(label: "macOS", value: receipt.macOSVersion)
185-
Divider().background(Color.white.opacity(0.06)).padding(.vertical, 4)
185+
Divider().background(Color.divider).padding(.vertical, 4)
186186
receiptRow(label: "Organization", value: receipt.organization)
187187
receiptRow(label: "Reference ID", value: receipt.referenceID)
188188
}
189189
.padding(16)
190190
}
191191
.background(
192192
RoundedRectangle(cornerRadius: 10)
193-
.fill(Color.white.opacity(0.03))
193+
.fill(Color.cardBackground)
194194
.overlay(
195195
RoundedRectangle(cornerRadius: 10)
196-
.stroke(Color.white.opacity(0.1), lineWidth: 1)
196+
.stroke(Color.cardBorder, lineWidth: 1)
197197
)
198198
)
199199
.frame(maxWidth: 500)
200200

201201
Text("Save or copy this receipt for your records. If you need temporary admin access in the future, contact \(config.supportContactName).")
202202
.font(.callout)
203-
.foregroundColor(.white.opacity(0.5))
203+
.foregroundColor(.textTertiary)
204204
.multilineTextAlignment(.center)
205205
.frame(maxWidth: 500)
206206

@@ -225,11 +225,11 @@ struct RemediationCompleteView: View {
225225

226226
// MARK: - Receipt Row
227227

228-
private func receiptRow(label: String, value: String, valueColor: Color = .white.opacity(0.85)) -> some View {
228+
private func receiptRow(label: String, value: String, valueColor: Color = .textPrimary) -> some View {
229229
HStack(alignment: .top) {
230230
Text(label)
231231
.font(.caption)
232-
.foregroundColor(.white.opacity(0.4))
232+
.foregroundColor(.textTertiary)
233233
.frame(width: 110, alignment: .trailing)
234234
Text(value)
235235
.font(.caption)
@@ -350,11 +350,11 @@ struct ErrorView: View {
350350
Text("Something Went Wrong")
351351
.font(.title2)
352352
.fontWeight(.bold)
353-
.foregroundColor(.white)
353+
.foregroundColor(.textPrimary)
354354

355355
Text(message)
356356
.font(.body)
357-
.foregroundColor(.white.opacity(0.7))
357+
.foregroundColor(.textSecondary)
358358
.multilineTextAlignment(.center)
359359
.frame(maxWidth: 450)
360360

@@ -398,7 +398,7 @@ struct PrimaryButtonStyle: ButtonStyle {
398398
.padding(.vertical, 12)
399399
.background(
400400
RoundedRectangle(cornerRadius: 10)
401-
.fill(Color.brandPrimary)
401+
.fill(Color.accent)
402402
.opacity(configuration.isPressed ? 0.7 : 1.0)
403403
)
404404
}
@@ -408,15 +408,15 @@ struct SecondaryButtonStyle: ButtonStyle {
408408
func makeBody(configuration: Configuration) -> some View {
409409
configuration.label
410410
.font(.headline)
411-
.foregroundColor(.white.opacity(0.9))
411+
.foregroundColor(.secondaryButtonText)
412412
.padding(.horizontal, 28)
413413
.padding(.vertical, 12)
414414
.background(
415415
RoundedRectangle(cornerRadius: 10)
416-
.stroke(Color.brandSecondary.opacity(0.4), lineWidth: 1)
416+
.stroke(Color.secondaryButtonBorder, lineWidth: 1)
417417
.background(
418418
RoundedRectangle(cornerRadius: 10)
419-
.fill(Color.brandSecondary.opacity(configuration.isPressed ? 0.2 : 0.1))
419+
.fill(Color.secondaryButtonBackground.opacity(configuration.isPressed ? 0.8 : 1.0))
420420
)
421421
)
422422
}
@@ -431,7 +431,7 @@ struct DangerButtonStyle: ButtonStyle {
431431
.padding(.vertical, 12)
432432
.background(
433433
RoundedRectangle(cornerRadius: 10)
434-
.fill(Color.brandPrimary)
434+
.fill(Color.accent)
435435
.opacity(configuration.isPressed ? 0.7 : 1.0)
436436
)
437437
}

AdminRightsManager/Models/AppConfiguration.swift

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,17 @@ struct AppConfiguration {
4545

4646
/// Support email for questions
4747
var supportEmail: String {
48-
defaults.string(forKey: "SupportEmail") ?? "helpdesk@example.com"
48+
defaults.string(forKey: "SupportEmail") ?? "helpdesk@example.edu"
4949
}
5050

5151
/// General IT website/portal URL (e.g., "https://it.example.edu")
5252
var supportWebsiteURL: String {
53-
defaults.string(forKey: "SupportWebsiteURL") ?? "https://www.google.com"
53+
defaults.string(forKey: "SupportWebsiteURL") ?? "https://it.example.edu"
5454
}
5555

5656
/// URL for submitting admin rights elevation requests (e.g., help desk ticket portal)
5757
var supportRequestURL: String {
58-
defaults.string(forKey: "SupportRequestURL") ?? "https://www.google.com"
58+
defaults.string(forKey: "SupportRequestURL") ?? "https://helpdesk.example.edu/request"
5959
}
6060

6161
/// Returns true if at least one support contact method is configured
@@ -65,7 +65,7 @@ struct AppConfiguration {
6565

6666
/// URL to the full admin rights policy document (shown as link in header)
6767
var policyURL: String {
68-
defaults.string(forKey: "PolicyURL") ?? "https://www.google.com"
68+
defaults.string(forKey: "PolicyURL") ?? "https://it.example.edu/policies/admin-rights"
6969
}
7070

7171
/// Custom policy message shown in the nag window
@@ -127,22 +127,14 @@ struct AppConfiguration {
127127
defaults.string(forKey: "DepartmentName") ?? "IT Services"
128128
}
129129

130-
/// Primary brand color hex (e.g., "#1b386d")
131-
/// Used for buttons, accents, info boxes
132-
var primaryColorHex: String {
133-
defaults.string(forKey: "PrimaryColorHex") ?? "#1b386d"
134-
}
135-
136-
/// Secondary/silver brand color hex (e.g., "#84888b")
137-
/// Used for secondary buttons and muted UI elements
138-
var secondaryColorHex: String {
139-
defaults.string(forKey: "SecondaryColorHex") ?? "#84888b"
140-
}
141-
142-
/// Dark brand color hex (e.g., "#172951")
143-
/// Used for backgrounds and deep UI surfaces
144-
var darkColorHex: String {
145-
defaults.string(forKey: "DarkColorHex") ?? "#172951"
130+
/// Accent/brand color hex (e.g., "#1b386d")
131+
/// Used for buttons, links, and accent elements. All other UI colors
132+
/// are derived automatically for light and dark mode.
133+
/// Falls back to legacy "PrimaryColorHex" key for backward compatibility.
134+
var accentColorHex: String {
135+
defaults.string(forKey: "AccentColorHex")
136+
?? defaults.string(forKey: "PrimaryColorHex")
137+
?? "#1b386d"
146138
}
147139

148140
/// Path to a custom logo image file on disk (PNG recommended)

0 commit comments

Comments
 (0)