feat: read "Занятия в Полоцке" as a day's town, show it in all clients
- Parser 14 (server/places.js): "Занятия в Полоцке/Новополоцке/г. Новополоцк" over a day's first row is no longer a lesson but a note of the day for its column (`places`); lessons of that day get `place`. A lesson below the note in the same cell keeps its own subject.
- A town on its own line ("Новополоцк", "г.Новополоцк") or after a room ("246(Новополоцк)") becomes the lesson's `place` and leaves the subject and room. "История Полоцка" is not a town.
- API 14: `place` on lessons and `places` on days of /api/timetable, `place` in the directory. The day notes are shared by the server and the PWA (shared/places.js); the same note in every subgroup reads as the whole group's.
- Android, PWA and iOS: a "Занятия в Полоцке" card above the day's lessons (day pages, calendar, overview, week view, desk stand), "Полоцк" under the date in the week grid, "249Ж · Полоцк" on lesson cards, a "Город" row in details, the town in widgets, tile, reminders and change lists.
- Snapshot lines show the town and the day notes; tests for notes, towns in cells and the API.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -142,6 +142,55 @@ struct Banner: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// "Занятия в Полоцке" above a day's lessons: Word marks the days a group spends in another
|
||||
/// town, and every lesson of that day is there.
|
||||
struct DayPlaces: View {
|
||||
var places: [DayPlace]
|
||||
|
||||
var body: some View {
|
||||
if !places.isEmpty {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
ForEach(places, id: \.self) { p in
|
||||
HStack(spacing: 14) {
|
||||
Image(systemName: "building.2")
|
||||
.font(.title3)
|
||||
.foregroundStyle(Color.indigo)
|
||||
.accessibilityHidden(true)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(p.text).font(.headline)
|
||||
Text(p.subgroup.map { "\($0) · пометка в Word на этот день" } ?? "Пометка в Word на этот день")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.background(Color.indigo.opacity(0.14), in: .rect(cornerRadius: 18, style: .continuous))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The town under a day's date in the week grid: short, in the note's colour.
|
||||
struct DayPlaceMark: View {
|
||||
var places: [DayPlace]
|
||||
|
||||
var body: some View {
|
||||
if let first = places.first {
|
||||
Text(first.place)
|
||||
.font(.caption2.weight(.semibold))
|
||||
.lineLimit(1)
|
||||
.padding(.horizontal, 4)
|
||||
.padding(.vertical, 1)
|
||||
.foregroundStyle(Color.indigo)
|
||||
.background(Color.indigo.opacity(0.14), in: .rect(cornerRadius: 4, style: .continuous))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The status banner of the app model, if it has something to say.
|
||||
struct StatusBanner: View {
|
||||
@Environment(AppModel.self) private var model
|
||||
|
||||
Reference in New Issue
Block a user