feat(cron): require job name

This commit is contained in:
Peter Steinberger
2025-12-20 19:56:49 +00:00
parent 9ae73e87eb
commit 2bcdf741f9
13 changed files with 150 additions and 25 deletions

View File

@@ -145,7 +145,8 @@ struct CronJobState: Codable, Equatable {
struct CronJob: Identifiable, Codable, Equatable {
let id: String
var name: String?
var name: String
var description: String?
var enabled: Bool
let createdAtMs: Int
let updatedAtMs: Int
@@ -157,7 +158,7 @@ struct CronJob: Identifiable, Codable, Equatable {
let state: CronJobState
var displayName: String {
let trimmed = (self.name ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
let trimmed = self.name.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? "Untitled job" : trimmed
}

View File

@@ -277,6 +277,9 @@ struct CronSettings: View {
private func detailCard(_ job: CronJob) -> some View {
VStack(alignment: .leading, spacing: 10) {
LabeledContent("Schedule") { Text(self.scheduleSummary(job.schedule)).font(.callout) }
if let desc = job.description, !desc.isEmpty {
LabeledContent("Description") { Text(desc).font(.callout) }
}
LabeledContent("Session") { Text(job.sessionTarget.rawValue) }
LabeledContent("Wake") { Text(job.wakeMode.rawValue) }
LabeledContent("Next run") {
@@ -514,6 +517,7 @@ struct CronJobEditor: View {
"Controls the label used when posting the completion summary back to the main session."
@State private var name: String = ""
@State private var description: String = ""
@State private var enabled: Bool = true
@State private var sessionTarget: CronSessionTarget = .main
@State private var wakeMode: CronWakeMode = .nextHeartbeat
@@ -554,7 +558,13 @@ struct CronJobEditor: View {
Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 14, verticalSpacing: 10) {
GridRow {
self.gridLabel("Name")
TextField("Optional label (e.g. “Daily summary”)", text: self.$name)
TextField("Required (e.g. “Daily summary”)", text: self.$name)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: .infinity)
}
GridRow {
self.gridLabel("Description")
TextField("Optional notes", text: self.$description)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: .infinity)
}
@@ -833,7 +843,8 @@ struct CronJobEditor: View {
private func hydrateFromJob() {
guard let job else { return }
self.name = job.name ?? ""
self.name = job.name
self.description = job.description ?? ""
self.enabled = job.enabled
self.sessionTarget = job.sessionTarget
self.wakeMode = job.wakeMode
@@ -881,6 +892,13 @@ struct CronJobEditor: View {
private func buildPayload() throws -> [String: AnyCodable] {
let name = self.name.trimmingCharacters(in: .whitespacesAndNewlines)
if name.isEmpty {
throw NSError(
domain: "Cron",
code: 0,
userInfo: [NSLocalizedDescriptionKey: "Name is required."])
}
let description = self.description.trimmingCharacters(in: .whitespacesAndNewlines)
let schedule: [String: Any]
switch self.scheduleKind {
case .at:
@@ -954,13 +972,14 @@ struct CronJobEditor: View {
}
var root: [String: Any] = [
"name": name,
"enabled": self.enabled,
"schedule": schedule,
"sessionTarget": self.sessionTarget.rawValue,
"wakeMode": self.wakeMode.rawValue,
"payload": payload,
]
if !name.isEmpty { root["name"] = name }
if !description.isEmpty { root["description"] = description }
if self.sessionTarget == .isolated {
let trimmed = self.postPrefix.trimmingCharacters(in: .whitespacesAndNewlines)
@@ -1035,6 +1054,7 @@ struct CronSettings_Previews: PreviewProvider {
CronJob(
id: "job-1",
name: "Daily summary",
description: nil,
enabled: true,
createdAtMs: 0,
updatedAtMs: 0,

View File

@@ -672,7 +672,8 @@ public struct SkillsUpdateParams: Codable {
public struct CronJob: Codable {
public let id: String
public let name: String?
public let name: String
public let description: String?
public let enabled: Bool
public let createdatms: Int
public let updatedatms: Int
@@ -685,7 +686,8 @@ public struct CronJob: Codable {
public init(
id: String,
name: String?,
name: String,
description: String?,
enabled: Bool,
createdatms: Int,
updatedatms: Int,
@@ -698,6 +700,7 @@ public struct CronJob: Codable {
) {
self.id = id
self.name = name
self.description = description
self.enabled = enabled
self.createdatms = createdatms
self.updatedatms = updatedatms
@@ -711,6 +714,7 @@ public struct CronJob: Codable {
private enum CodingKeys: String, CodingKey {
case id
case name
case description
case enabled
case createdatms = "createdAtMs"
case updatedatms = "updatedAtMs"
@@ -740,7 +744,8 @@ public struct CronStatusParams: Codable {
}
public struct CronAddParams: Codable {
public let name: String?
public let name: String
public let description: String?
public let enabled: Bool?
public let schedule: AnyCodable
public let sessiontarget: AnyCodable
@@ -749,7 +754,8 @@ public struct CronAddParams: Codable {
public let isolation: [String: AnyCodable]?
public init(
name: String?,
name: String,
description: String?,
enabled: Bool?,
schedule: AnyCodable,
sessiontarget: AnyCodable,
@@ -758,6 +764,7 @@ public struct CronAddParams: Codable {
isolation: [String: AnyCodable]?
) {
self.name = name
self.description = description
self.enabled = enabled
self.schedule = schedule
self.sessiontarget = sessiontarget
@@ -767,6 +774,7 @@ public struct CronAddParams: Codable {
}
private enum CodingKeys: String, CodingKey {
case name
case description
case enabled
case schedule
case sessiontarget = "sessionTarget"

View File

@@ -61,6 +61,7 @@ struct CronModelsTests {
let base = CronJob(
id: "x",
name: " hello ",
description: nil,
enabled: true,
createdAtMs: 0,
updatedAtMs: 0,
@@ -81,6 +82,7 @@ struct CronModelsTests {
let job = CronJob(
id: "x",
name: "t",
description: nil,
enabled: true,
createdAtMs: 0,
updatedAtMs: 0,