added fixture scheduling
This commit is contained in:
543
internal/view/seasonsview/fixture_detail.templ
Normal file
543
internal/view/seasonsview/fixture_detail.templ
Normal file
@@ -0,0 +1,543 @@
|
||||
package seasonsview
|
||||
|
||||
import "git.haelnorr.com/h/oslstats/internal/db"
|
||||
import "git.haelnorr.com/h/oslstats/internal/permissions"
|
||||
import "git.haelnorr.com/h/oslstats/internal/contexts"
|
||||
import "git.haelnorr.com/h/oslstats/internal/view/baseview"
|
||||
import "fmt"
|
||||
import "time"
|
||||
|
||||
func formatScheduleTime(t *time.Time) string {
|
||||
if t == nil {
|
||||
return "No time set"
|
||||
}
|
||||
return t.Format("Mon 2 Jan 2006 at 3:04 PM")
|
||||
}
|
||||
|
||||
func formatHistoryTime(unix int64) string {
|
||||
return time.Unix(unix, 0).Format("2 Jan 2006 15:04")
|
||||
}
|
||||
|
||||
templ FixtureDetailPage(
|
||||
fixture *db.Fixture,
|
||||
currentSchedule *db.FixtureSchedule,
|
||||
history []*db.FixtureSchedule,
|
||||
canSchedule bool,
|
||||
userTeamID int,
|
||||
) {
|
||||
{{
|
||||
permCache := contexts.Permissions(ctx)
|
||||
canManage := permCache.HasPermission(permissions.FixturesManage)
|
||||
backURL := fmt.Sprintf("/seasons/%s/leagues/%s/fixtures", fixture.Season.ShortName, fixture.League.ShortName)
|
||||
}}
|
||||
@baseview.Layout(fmt.Sprintf("%s vs %s", fixture.HomeTeam.Name, fixture.AwayTeam.Name)) {
|
||||
<div class="max-w-screen-lg mx-auto px-4 py-8">
|
||||
<!-- Header -->
|
||||
<div class="bg-mantle border border-surface1 rounded-lg overflow-hidden mb-6">
|
||||
<div class="bg-surface0 border-b border-surface1 px-6 py-8">
|
||||
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-2">
|
||||
<h1 class="text-3xl font-bold text-text">
|
||||
{ fixture.HomeTeam.Name }
|
||||
<span class="text-subtext0 font-normal">vs</span>
|
||||
{ fixture.AwayTeam.Name }
|
||||
</h1>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<span class="px-2 py-0.5 bg-surface1 text-subtext0 rounded text-xs font-mono">
|
||||
Round { fmt.Sprint(fixture.Round) }
|
||||
</span>
|
||||
if fixture.GameWeek != nil {
|
||||
<span class="px-2 py-0.5 bg-surface1 text-subtext0 rounded text-xs font-mono">
|
||||
Game Week { fmt.Sprint(*fixture.GameWeek) }
|
||||
</span>
|
||||
}
|
||||
<span class="text-subtext1 text-sm">
|
||||
{ fixture.Season.Name } — { fixture.League.Name }
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
href={ templ.SafeURL(backURL) }
|
||||
class="rounded-lg px-4 py-2 hover:cursor-pointer text-center
|
||||
bg-surface1 hover:bg-surface2 text-text transition"
|
||||
>
|
||||
Back to Fixtures
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Schedule Status + Actions -->
|
||||
<div class="space-y-6">
|
||||
@fixtureScheduleStatus(fixture, currentSchedule, canSchedule, canManage, userTeamID)
|
||||
@fixtureScheduleActions(fixture, currentSchedule, canSchedule, canManage, userTeamID)
|
||||
@fixtureScheduleHistory(fixture, history)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
templ fixtureScheduleStatus(
|
||||
fixture *db.Fixture,
|
||||
current *db.FixtureSchedule,
|
||||
canSchedule bool,
|
||||
canManage bool,
|
||||
userTeamID int,
|
||||
) {
|
||||
<div class="bg-mantle border border-surface1 rounded-lg overflow-hidden">
|
||||
<div class="bg-surface0 border-b border-surface1 px-4 py-3">
|
||||
<h2 class="text-lg font-bold text-text">Schedule Status</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
if current == nil {
|
||||
<!-- No schedule yet -->
|
||||
<div class="text-center py-4">
|
||||
<div class="text-4xl mb-3">📅</div>
|
||||
<p class="text-lg text-text font-medium">No time scheduled</p>
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
if canSchedule {
|
||||
Use the form to propose a time for this fixture.
|
||||
} else {
|
||||
A team manager needs to propose a time for this fixture.
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
} else if current.Status == db.ScheduleStatusPending && current.ScheduledTime != nil {
|
||||
<!-- Pending proposal with time -->
|
||||
<div class="text-center py-4">
|
||||
<div class="text-4xl mb-3">⏳</div>
|
||||
<p class="text-lg text-text font-medium">
|
||||
Proposed: { formatScheduleTime(current.ScheduledTime) }
|
||||
</p>
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
Proposed by
|
||||
<span class="text-text font-medium">{ current.ProposedBy.Name }</span>
|
||||
— awaiting response from the other team
|
||||
</p>
|
||||
if canSchedule && userTeamID != current.ProposedByTeamID {
|
||||
<div class="flex justify-center gap-3 mt-4">
|
||||
<form
|
||||
hx-post={ fmt.Sprintf("/fixtures/%d/schedule/%d/accept", fixture.ID, current.ID) }
|
||||
hx-swap="none"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-4 py-2 bg-green hover:bg-green/75 text-mantle rounded-lg
|
||||
font-medium transition hover:cursor-pointer"
|
||||
>
|
||||
Accept
|
||||
</button>
|
||||
</form>
|
||||
<form
|
||||
hx-post={ fmt.Sprintf("/fixtures/%d/schedule/%d/reject", fixture.ID, current.ID) }
|
||||
hx-swap="none"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-4 py-2 bg-red hover:bg-red/80 text-mantle rounded-lg
|
||||
font-medium transition hover:cursor-pointer"
|
||||
>
|
||||
Reject
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
if canSchedule && userTeamID == current.ProposedByTeamID {
|
||||
<div class="flex justify-center mt-4">
|
||||
<button
|
||||
type="button"
|
||||
@click={ "window.dispatchEvent(new CustomEvent('confirm-action', { detail: { title: 'Withdraw Proposal', message: 'Are you sure you want to withdraw your proposed time?', action: () => htmx.ajax('POST', '/fixtures/" + fmt.Sprint(fixture.ID) + "/schedule/" + fmt.Sprint(current.ID) + "/withdraw', { swap: 'none' }) } }))" }
|
||||
class="px-4 py-2 bg-surface1 hover:bg-surface2 text-text rounded-lg
|
||||
font-medium transition hover:cursor-pointer"
|
||||
>
|
||||
Withdraw Proposal
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} else if current.Status == db.ScheduleStatusAccepted {
|
||||
<!-- Accepted / Confirmed -->
|
||||
<div class="text-center py-4">
|
||||
<div class="text-4xl mb-3">✅</div>
|
||||
<p class="text-lg text-green font-medium">
|
||||
Confirmed: { formatScheduleTime(current.ScheduledTime) }
|
||||
</p>
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
Both teams have agreed on this time.
|
||||
</p>
|
||||
</div>
|
||||
} else if current.Status == db.ScheduleStatusRejected {
|
||||
<!-- Rejected -->
|
||||
<div class="text-center py-4">
|
||||
<div class="text-4xl mb-3">❌</div>
|
||||
<p class="text-lg text-red font-medium">Proposal Rejected</p>
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
The proposed time was rejected. A new time needs to be proposed.
|
||||
</p>
|
||||
</div>
|
||||
} else if current.Status == db.ScheduleStatusCancelled {
|
||||
<!-- Cancelled / Forfeit -->
|
||||
<div class="text-center py-4">
|
||||
<div class="text-4xl mb-3">🚫</div>
|
||||
<p class="text-lg text-red font-medium">Fixture Forfeited</p>
|
||||
if current.RescheduleReason != nil {
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
{ *current.RescheduleReason }
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
} else if current.Status == db.ScheduleStatusRescheduled {
|
||||
<!-- Rescheduled (terminal - new proposal should follow) -->
|
||||
<div class="text-center py-4">
|
||||
<div class="text-4xl mb-3">🔄</div>
|
||||
<p class="text-lg text-yellow font-medium">Rescheduled</p>
|
||||
if current.RescheduleReason != nil {
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
Reason: { *current.RescheduleReason }
|
||||
</p>
|
||||
}
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
A new time needs to be proposed.
|
||||
</p>
|
||||
</div>
|
||||
} else if current.Status == db.ScheduleStatusPostponed {
|
||||
<!-- Postponed (terminal - new proposal should follow) -->
|
||||
<div class="text-center py-4">
|
||||
<div class="text-4xl mb-3">⏸️</div>
|
||||
<p class="text-lg text-peach font-medium">Postponed</p>
|
||||
if current.RescheduleReason != nil {
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
Reason: { *current.RescheduleReason }
|
||||
</p>
|
||||
}
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
A new time needs to be proposed.
|
||||
</p>
|
||||
</div>
|
||||
} else if current.Status == db.ScheduleStatusWithdrawn {
|
||||
<!-- Withdrawn -->
|
||||
<div class="text-center py-4">
|
||||
<div class="text-4xl mb-3">↩️</div>
|
||||
<p class="text-lg text-subtext0 font-medium">Proposal Withdrawn</p>
|
||||
<p class="text-sm text-subtext1 mt-1">
|
||||
The proposed time was withdrawn. A new time needs to be proposed.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ fixtureScheduleActions(
|
||||
fixture *db.Fixture,
|
||||
current *db.FixtureSchedule,
|
||||
canSchedule bool,
|
||||
canManage bool,
|
||||
userTeamID int,
|
||||
) {
|
||||
{{
|
||||
// Determine what actions are available
|
||||
showPropose := false
|
||||
showReschedule := false
|
||||
showPostpone := false
|
||||
showCancel := false
|
||||
|
||||
if canSchedule {
|
||||
if current == nil {
|
||||
showPropose = true
|
||||
} else if current.Status == db.ScheduleStatusRejected {
|
||||
showPropose = true
|
||||
} else if current.Status == db.ScheduleStatusRescheduled {
|
||||
showPropose = true
|
||||
} else if current.Status == db.ScheduleStatusPostponed {
|
||||
showPropose = true
|
||||
} else if current.Status == db.ScheduleStatusWithdrawn {
|
||||
showPropose = true
|
||||
} else if current.Status == db.ScheduleStatusAccepted {
|
||||
showReschedule = true
|
||||
showPostpone = true
|
||||
}
|
||||
}
|
||||
if canManage && current != nil && !current.Status.IsTerminal() {
|
||||
showCancel = true
|
||||
}
|
||||
}}
|
||||
if showPropose || showReschedule || showPostpone || showCancel {
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- Propose Time -->
|
||||
if showPropose {
|
||||
<div class="bg-mantle border border-surface1 rounded-lg">
|
||||
<div class="bg-surface0 border-b border-surface1 px-4 py-3 rounded-t-lg">
|
||||
<h3 class="text-md font-bold text-text">Propose Time</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<form
|
||||
hx-post={ fmt.Sprintf("/fixtures/%d/schedule", fixture.ID) }
|
||||
hx-swap="none"
|
||||
class="space-y-4"
|
||||
>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-subtext0 mb-1">Date & Time</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
name="scheduled_time"
|
||||
required
|
||||
class="w-full px-3 py-2 bg-base border border-surface1 rounded-lg text-text
|
||||
focus:border-blue focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full px-4 py-2 bg-blue hover:bg-blue/80 text-mantle rounded-lg
|
||||
font-medium transition hover:cursor-pointer"
|
||||
>
|
||||
Propose Time
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<!-- Reschedule -->
|
||||
if showReschedule {
|
||||
<div class="bg-mantle border border-surface1 rounded-lg">
|
||||
<div class="bg-surface0 border-b border-surface1 px-4 py-3 rounded-t-lg">
|
||||
<h3 class="text-md font-bold text-text">Reschedule</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<form
|
||||
hx-post={ fmt.Sprintf("/fixtures/%d/schedule/reschedule", fixture.ID) }
|
||||
hx-swap="none"
|
||||
class="space-y-4"
|
||||
>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-subtext0 mb-1">New Date & Time</label>
|
||||
<input
|
||||
type="datetime-local"
|
||||
name="scheduled_time"
|
||||
required
|
||||
class="w-full px-3 py-2 bg-base border border-surface1 rounded-lg text-text
|
||||
focus:border-blue focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-subtext0 mb-1">Reason</label>
|
||||
@rescheduleReasonSelect(fixture)
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full px-4 py-2 bg-yellow hover:bg-yellow/80 text-mantle rounded-lg
|
||||
font-medium transition hover:cursor-pointer"
|
||||
>
|
||||
Reschedule
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<!-- Postpone -->
|
||||
if showPostpone {
|
||||
<div class="bg-mantle border border-surface1 rounded-lg">
|
||||
<div class="bg-surface0 border-b border-surface1 px-4 py-3 rounded-t-lg">
|
||||
<h3 class="text-md font-bold text-text">Postpone</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-subtext0 mb-1">Reason</label>
|
||||
@rescheduleReasonSelect(fixture)
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
@click={ fmt.Sprintf("if (!$el.parentElement.querySelector('select').value) { return; } window.dispatchEvent(new CustomEvent('confirm-action', { detail: { title: 'Postpone Fixture', message: 'Are you sure you want to postpone this fixture? The current schedule will be cleared.', action: () => htmx.ajax('POST', '/fixtures/%d/schedule/postpone', { swap: 'none', values: { reschedule_reason: $el.parentElement.querySelector('select').value } }) } }))", fixture.ID) }
|
||||
class="w-full px-4 py-2 bg-peach hover:bg-peach/80 text-mantle rounded-lg
|
||||
font-medium transition hover:cursor-pointer"
|
||||
>
|
||||
Postpone Fixture
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<!-- Declare Forfeit (moderator only) -->
|
||||
if showCancel {
|
||||
<div class="bg-mantle border border-red/30 rounded-lg">
|
||||
<div class="bg-red/10 border-b border-red/30 px-4 py-3 rounded-t-lg">
|
||||
<h3 class="text-md font-bold text-red">Declare Forfeit</h3>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<p class="text-xs text-red/80 mb-3 font-medium">
|
||||
This action is irreversible. Declaring a forfeit will permanently cancel the fixture schedule.
|
||||
</p>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-subtext0 mb-1">Forfeiting Team</label>
|
||||
@forfeitReasonSelect(fixture)
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
@click={ fmt.Sprintf("if (!$el.parentElement.querySelector('select').value) { return; } window.dispatchEvent(new CustomEvent('confirm-action', { detail: { title: 'Declare Forfeit', message: 'This action is IRREVERSIBLE. The fixture schedule will be permanently cancelled. Are you sure?', action: () => htmx.ajax('POST', '/fixtures/%d/schedule/cancel', { swap: 'none', values: { reschedule_reason: $el.parentElement.querySelector('select').value } }) } }))", fixture.ID) }
|
||||
class="w-full px-4 py-2 bg-red hover:bg-red/80 text-mantle rounded-lg
|
||||
font-medium transition hover:cursor-pointer"
|
||||
>
|
||||
Declare Forfeit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} else {
|
||||
if !canSchedule && !canManage {
|
||||
<div class="bg-mantle border border-surface1 rounded-lg p-6 text-center">
|
||||
<p class="text-subtext1 text-sm">
|
||||
Only team managers can manage fixture scheduling.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
templ forfeitReasonSelect(fixture *db.Fixture) {
|
||||
<select
|
||||
name="reschedule_reason"
|
||||
required
|
||||
class="w-full px-3 py-2 bg-base border border-surface1 rounded-lg text-text
|
||||
focus:border-blue focus:outline-none hover:cursor-pointer"
|
||||
>
|
||||
<option value="" disabled selected>Select forfeiting team</option>
|
||||
<option value={ fmt.Sprintf("%s Forfeit", fixture.HomeTeam.Name) }>
|
||||
{ fixture.HomeTeam.Name } Forfeit
|
||||
</option>
|
||||
<option value={ fmt.Sprintf("%s Forfeit", fixture.AwayTeam.Name) }>
|
||||
{ fixture.AwayTeam.Name } Forfeit
|
||||
</option>
|
||||
</select>
|
||||
}
|
||||
|
||||
templ rescheduleReasonSelect(fixture *db.Fixture) {
|
||||
<select
|
||||
name="reschedule_reason"
|
||||
required
|
||||
class="w-full px-3 py-2 bg-base border border-surface1 rounded-lg text-text
|
||||
focus:border-blue focus:outline-none hover:cursor-pointer"
|
||||
>
|
||||
<option value="" disabled selected>Select a reason</option>
|
||||
<option value="Mutually Agreed">Mutually Agreed</option>
|
||||
<option value={ fmt.Sprintf("%s Unavailable", fixture.HomeTeam.Name) }>
|
||||
{ fixture.HomeTeam.Name } Unavailable
|
||||
</option>
|
||||
<option value={ fmt.Sprintf("%s Unavailable", fixture.AwayTeam.Name) }>
|
||||
{ fixture.AwayTeam.Name } Unavailable
|
||||
</option>
|
||||
<option value={ fmt.Sprintf("%s No-show", fixture.HomeTeam.Name) }>
|
||||
{ fixture.HomeTeam.Name } No-show
|
||||
</option>
|
||||
<option value={ fmt.Sprintf("%s No-show", fixture.AwayTeam.Name) }>
|
||||
{ fixture.AwayTeam.Name } No-show
|
||||
</option>
|
||||
</select>
|
||||
}
|
||||
|
||||
templ fixtureScheduleHistory(fixture *db.Fixture, history []*db.FixtureSchedule) {
|
||||
<div class="bg-mantle border border-surface1 rounded-lg overflow-hidden">
|
||||
<div class="bg-surface0 border-b border-surface1 px-4 py-3">
|
||||
<h2 class="text-lg font-bold text-text">Schedule History</h2>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
if len(history) == 0 {
|
||||
<p class="text-subtext1 text-sm text-center py-4">No scheduling activity yet.</p>
|
||||
} else {
|
||||
<div class="space-y-3">
|
||||
for i := len(history) - 1; i >= 0; i-- {
|
||||
@scheduleHistoryItem(history[i], i == len(history)-1)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ scheduleHistoryItem(schedule *db.FixtureSchedule, isCurrent bool) {
|
||||
{{
|
||||
statusColor := "text-subtext0"
|
||||
statusBg := "bg-surface1"
|
||||
statusLabel := string(schedule.Status)
|
||||
switch schedule.Status {
|
||||
case db.ScheduleStatusPending:
|
||||
statusColor = "text-blue"
|
||||
statusBg = "bg-blue/20"
|
||||
statusLabel = "Pending"
|
||||
case db.ScheduleStatusAccepted:
|
||||
statusColor = "text-green"
|
||||
statusBg = "bg-green/20"
|
||||
statusLabel = "Accepted"
|
||||
case db.ScheduleStatusRejected:
|
||||
statusColor = "text-red"
|
||||
statusBg = "bg-red/20"
|
||||
statusLabel = "Rejected"
|
||||
case db.ScheduleStatusRescheduled:
|
||||
statusColor = "text-yellow"
|
||||
statusBg = "bg-yellow/20"
|
||||
statusLabel = "Rescheduled"
|
||||
case db.ScheduleStatusPostponed:
|
||||
statusColor = "text-peach"
|
||||
statusBg = "bg-peach/20"
|
||||
statusLabel = "Postponed"
|
||||
case db.ScheduleStatusCancelled:
|
||||
statusColor = "text-red"
|
||||
statusBg = "bg-red/20"
|
||||
statusLabel = "Cancelled"
|
||||
case db.ScheduleStatusWithdrawn:
|
||||
statusColor = "text-subtext0"
|
||||
statusBg = "bg-surface1"
|
||||
statusLabel = "Withdrawn"
|
||||
}
|
||||
}}
|
||||
<div class={ "border rounded-lg p-3", templ.KV("border-surface1", !isCurrent), templ.KV("border-blue/30 bg-blue/5", isCurrent) }>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
if isCurrent {
|
||||
<span class="text-xs px-1.5 py-0.5 bg-blue/20 text-blue rounded font-medium">
|
||||
CURRENT
|
||||
</span>
|
||||
}
|
||||
<span class={ "text-xs px-2 py-0.5 rounded font-medium", statusBg, statusColor }>
|
||||
{ statusLabel }
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-xs text-subtext1">
|
||||
{ formatHistoryTime(schedule.CreatedAt) }
|
||||
</span>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="text-subtext0">Proposed by:</span>
|
||||
<span class="text-text font-medium">{ schedule.ProposedBy.Name }</span>
|
||||
</div>
|
||||
if schedule.ScheduledTime != nil {
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="text-subtext0">Time:</span>
|
||||
<span class="text-text">{ formatScheduleTime(schedule.ScheduledTime) }</span>
|
||||
</div>
|
||||
} else {
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="text-subtext0">Time:</span>
|
||||
<span class="text-subtext1 italic">No time set</span>
|
||||
</div>
|
||||
}
|
||||
if schedule.AcceptedBy != nil {
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="text-subtext0">Accepted by:</span>
|
||||
<span class="text-text font-medium">{ schedule.AcceptedBy.Name }</span>
|
||||
</div>
|
||||
}
|
||||
if schedule.RescheduleReason != nil {
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="text-subtext0">Reason:</span>
|
||||
<span class="text-text">{ *schedule.RescheduleReason }</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
Reference in New Issue
Block a user