chore: merge main into feat/tzaware
This commit is contained in:
commit
c4b9cb20f1
|
@ -4,6 +4,8 @@ n-theme
|
|||
.app-error(v-if='siteStore.criticalError')
|
||||
i.bi.bi-x-octagon-fill.me-2
|
||||
span {{siteStore.criticalError}}
|
||||
.app-error-link(v-if='siteStore.criticalError && siteStore.criticalErrorLink')
|
||||
a(:href='siteStore.criticalErrorLink') {{siteStore.criticalErrorLinkText}} #[i.bi.bi-arrow-right-square-fill.ms-2]
|
||||
.app-container(ref='appContainer')
|
||||
router-view.meeting
|
||||
</template>
|
||||
|
@ -56,4 +58,23 @@ onBeforeUnmount(() => {
|
|||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-error-link {
|
||||
background-color: lighten($red-100, 5%);
|
||||
border-radius: 0 0 5px 5px;
|
||||
color: #FFF;
|
||||
font-weight: 500;
|
||||
font-size: .9em;
|
||||
padding: .7rem 1rem;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: $red-700;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover, &:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -152,6 +152,7 @@ import {
|
|||
} from 'naive-ui'
|
||||
|
||||
import { useAgendaStore } from './store'
|
||||
import { getUrl } from '../shared/urls'
|
||||
|
||||
// PROPS
|
||||
|
||||
|
@ -205,10 +206,22 @@ const eventDetails = computed(() => {
|
|||
title: props.event.type === 'regular' ? `${props.event.groupName} (${props.event.acronym})` : props.event.name,
|
||||
showAgenda: props.event.flags.showAgenda,
|
||||
materialsUrl: materialsUrl,
|
||||
detailsUrl: `/meeting/${agendaStore.meeting.number}/session/${props.event.acronym}/`,
|
||||
tarUrl: `/meeting/${agendaStore.meeting.number}/agenda/${props.event.acronym}-drafts.tgz`,
|
||||
pdfUrl: `/meeting/${agendaStore.meeting.number}/agenda/${props.event.acronym}-drafts.pdf`,
|
||||
notepadUrl: `https://notes.ietf.org/notes-ietf-${agendaStore.meeting.number}-${props.event.type === 'plenary' ? 'plenary' : props.event.acronym}`,
|
||||
detailsUrl: getUrl('meetingDetails', {
|
||||
meetingNumber: agendaStore.meeting.number,
|
||||
eventAcronym: props.event.acronym
|
||||
}),
|
||||
tarUrl: getUrl('meetingMaterialsTar', {
|
||||
meetingNumber: agendaStore.meeting.number,
|
||||
eventAcronym: props.event.acronym
|
||||
}),
|
||||
pdfUrl: getUrl('meetingMaterialsPdf', {
|
||||
meetingNumber: agendaStore.meeting.number,
|
||||
eventAcronym: props.event.acronym
|
||||
}),
|
||||
notepadUrl: getUrl('meetingNotes', {
|
||||
meetingNumber: agendaStore.meeting.number,
|
||||
eventAcronym: props.event.type === 'plenary' ? 'plenary' : props.event.acronym
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ n-drawer(v-model:show='state.isShown', placement='bottom', :height='state.drawer
|
|||
)
|
||||
template(#trigger)
|
||||
span.badge BoF
|
||||
span #[a(href='https://www.ietf.org/how/bofs/', target='_blank') Birds of a Feather] sessions (BoFs) are initial discussions about a particular topic of interest to the IETF community.
|
||||
span #[a(:href='getUrl(`bofDefinition`)', target='_blank') Birds of a Feather] sessions (BoFs) are initial discussions about a particular topic of interest to the IETF community.
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
@ -77,6 +77,7 @@ import {
|
|||
} from 'naive-ui'
|
||||
|
||||
import { useAgendaStore } from './store'
|
||||
import { getUrl } from '../shared/urls'
|
||||
|
||||
// STORES
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
|
||||
import { useAgendaStore } from './store'
|
||||
import { useSiteStore } from '../shared/store';
|
||||
import { getUrl } from '../shared/urls'
|
||||
|
||||
// MESSAGE PROVIDER
|
||||
|
||||
|
@ -64,11 +65,11 @@ function downloadIcs (key) {
|
|||
let icsUrl = ''
|
||||
if (agendaStore.pickerMode) {
|
||||
const sessionKeywords = agendaStore.scheduleAdjusted.map(s => s.sessionKeyword)
|
||||
icsUrl = `/meeting/${agendaStore.meeting.number}/agenda.ics?show=${sessionKeywords.join(',')}`
|
||||
icsUrl = `${getUrl('meetingCalIcs', { meetingNumber: agendaStore.meeting.number })}?show=${sessionKeywords.join(',')}`
|
||||
} else if (agendaStore.selectedCatSubs.length > 0) {
|
||||
icsUrl = `/meeting/${agendaStore.meeting.number}/agenda.ics?show=${agendaStore.selectedCatSubs.join(',')}`
|
||||
icsUrl = `${getUrl('meetingCalIcs', { meetingNumber: agendaStore.meeting.number })}?show=${agendaStore.selectedCatSubs.join(',')}`
|
||||
} else {
|
||||
icsUrl = `/meeting/${agendaStore.meeting.number}/agenda.ics`
|
||||
icsUrl = `${getUrl('meetingCalIcs', { meetingNumber: agendaStore.meeting.number })}`
|
||||
}
|
||||
if (key === 'subscribe') {
|
||||
window.location.assign(`webcal://${window.location.host}${icsUrl}`)
|
||||
|
|
|
@ -120,7 +120,8 @@ import {
|
|||
} from 'naive-ui'
|
||||
|
||||
import { useAgendaStore } from './store'
|
||||
import { useSiteStore } from '../shared/store';
|
||||
import { useSiteStore } from '../shared/store'
|
||||
import { getUrl } from '../shared/urls'
|
||||
|
||||
// MESSAGE PROVIDER
|
||||
|
||||
|
@ -181,11 +182,11 @@ function downloadIcs (key) {
|
|||
let icsUrl = ''
|
||||
if (agendaStore.pickerMode) {
|
||||
const sessionKeywords = agendaStore.scheduleAdjusted.map(s => s.sessionKeyword)
|
||||
icsUrl = `/meeting/${agendaStore.meeting.number}/agenda.ics?show=${sessionKeywords.join(',')}`
|
||||
icsUrl = `${getUrl('meetingCalIcs', { meetingNumber: agendaStore.meeting.number })}?show=${sessionKeywords.join(',')}`
|
||||
} else if (agendaStore.selectedCatSubs.length > 0) {
|
||||
icsUrl = `/meeting/${agendaStore.meeting.number}/agenda.ics?show=${agendaStore.selectedCatSubs.join(',')}`
|
||||
icsUrl = `${getUrl('meetingCalIcs', { meetingNumber: agendaStore.meeting.number })}?show=${agendaStore.selectedCatSubs.join(',')}`
|
||||
} else {
|
||||
icsUrl = `/meeting/${agendaStore.meeting.number}/agenda.ics`
|
||||
icsUrl = `${getUrl('meetingCalIcs', { meetingNumber: agendaStore.meeting.number })}`
|
||||
}
|
||||
if (key === 'subscribe') {
|
||||
window.location.assign(`webcal://${window.location.host}${icsUrl}`)
|
||||
|
|
|
@ -202,6 +202,7 @@ import AgendaDetailsModal from './AgendaDetailsModal.vue'
|
|||
|
||||
import { useAgendaStore } from './store'
|
||||
import { useSiteStore } from '../shared/store'
|
||||
import { getUrl } from '../shared/urls'
|
||||
|
||||
// MESSAGE PROVIDER
|
||||
|
||||
|
@ -275,14 +276,20 @@ const meetingEvents = computed(() => {
|
|||
id: `lnk-${item.id}-tar`,
|
||||
label: 'Download meeting materials as .tar archive',
|
||||
icon: 'file-zip',
|
||||
href: `/meeting/${agendaStore.meeting.number}/agenda/${item.acronym}-drafts.tgz`,
|
||||
href: getUrl('meetingMaterialsTar', {
|
||||
meetingNumber: agendaStore.meeting.number,
|
||||
eventAcronym: item.acronym
|
||||
}),
|
||||
color: 'brown'
|
||||
})
|
||||
links.push({
|
||||
id: `lnk-${item.id}-pdf`,
|
||||
label: 'Download meeting materials as PDF file',
|
||||
icon: 'file-pdf',
|
||||
href: `/meeting/${agendaStore.meeting.number}/agenda/${item.acronym}-drafts.pdf`,
|
||||
href: getUrl('meetingMaterialsPdf', {
|
||||
meetingNumber: agendaStore.meeting.number,
|
||||
eventAcronym: item.acronym
|
||||
}),
|
||||
color: 'red'
|
||||
})
|
||||
}
|
||||
|
@ -291,7 +298,10 @@ const meetingEvents = computed(() => {
|
|||
id: `lnk-${item.id}-note`,
|
||||
label: 'Notepad for note-takers',
|
||||
icon: 'journal-text',
|
||||
href: `https://notes.ietf.org/notes-ietf-${agendaStore.meeting.number}-${item.type === 'plenary' ? 'plenary' : item.acronym}`,
|
||||
href: getUrl('meetingNotes', {
|
||||
meetingNumber: agendaStore.meeting.number,
|
||||
eventAcronym: item.type === 'plenary' ? 'plenary' : item.acronym
|
||||
}),
|
||||
color: 'blue'
|
||||
})
|
||||
}
|
||||
|
@ -404,7 +414,10 @@ const meetingEvents = computed(() => {
|
|||
id: `lnk-${item.id}-rec`,
|
||||
label: 'Session recording',
|
||||
icon: 'film',
|
||||
href: `https://www.meetecho.com/ietf${agendaStore.meeting.number}/recordings#${item.acronym.toUpperCase()}`,
|
||||
href: getUrl('meetingMeetechoRecordings', {
|
||||
meetingNumber: agendaStore.meeting.number,
|
||||
eventAcronym: item.acronym.toUpperCase()
|
||||
}),
|
||||
color: 'purple'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -183,7 +183,9 @@ export const useAgendaStore = defineStore('agenda', {
|
|||
console.error(err)
|
||||
const siteStore = useSiteStore()
|
||||
siteStore.$patch({
|
||||
criticalError: `Failed to load this meeting: ${err.message}`
|
||||
criticalError: `Failed to load this meeting: ${err.message}`,
|
||||
criticalErrorLink: meetingNumber ? `/meeting/${meetingNumber}/agenda.txt` : `/meeting/agenda.txt`,
|
||||
criticalErrorLinkText: 'Switch to text-only agenda version'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ import { defineStore } from 'pinia'
|
|||
export const useSiteStore = defineStore('site', {
|
||||
state: () => ({
|
||||
criticalError: null,
|
||||
criticalErrorLink: null,
|
||||
criticalErrorLinkText: null,
|
||||
isMobile: /Mobi/i.test(navigator.userAgent),
|
||||
viewport: Math.round(window.innerWidth)
|
||||
})
|
||||
|
|
30
client/shared/urls.js
Normal file
30
client/shared/urls.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import template from 'lodash-es/template'
|
||||
import transform from 'lodash-es/transform'
|
||||
|
||||
const urls = {
|
||||
bofDefinition: 'https://www.ietf.org/how/bofs/',
|
||||
meetingCalIcs: '/meeting/{meetingNumber}/agenda.ics',
|
||||
meetingDetails: '/meeting/{meetingNumber}/session/{eventAcronym}/',
|
||||
meetingMaterialsPdf: '/meeting/{meetingNumber}/agenda/{eventAcronym}-drafts.pdf',
|
||||
meetingMaterialsTar: '/meeting/{meetingNumber}/agenda/{eventAcronym}-drafts.tgz',
|
||||
meetingMeetechoRecordings: 'https://www.meetecho.com/ietf{meetingNumber}/recordings#{eventAcronym}',
|
||||
meetingNotes: 'https://notes.ietf.org/notes-ietf-{meetingNumber}-{eventAcronym}'
|
||||
}
|
||||
|
||||
const interpolate = /{([\s\S]+?)}/g
|
||||
const compiled = transform(urls, (result, value, key) => {
|
||||
result[key] = template(value, { interpolate })
|
||||
}, {})
|
||||
|
||||
/**
|
||||
* Get an URL and replace tokens with provided values.
|
||||
*
|
||||
* @param {string} key The key of the URL template to use.
|
||||
* @param {Object} [tokens] An object of tokens to replace in the URL template.
|
||||
* @returns {string} URL with tokens replaced with the provided values.
|
||||
*/
|
||||
export const getUrl = (key, tokens = {}) => {
|
||||
if (!key) { throw new Error('Must provide a key for getUrl()') }
|
||||
if (!compiled[key]) { throw new Error('Invalid getUrl() key') }
|
||||
return compiled[key](tokens)
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
{% if template_list %}
|
||||
<ul>
|
||||
{% for template in template_list %}
|
||||
<li><a href="{% url "ietf.dbtemplate.views.template_edit" group.acronym template.id %}">{{ template }}</a></li>
|
||||
<li><a href="{% url "ietf.dbtemplate.views.group_template_edit" group.acronym template.id %}">{{ template }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
<h2>Template content</h2>
|
||||
|
||||
<div class="card">
|
||||
<p class="pasted">{{ template.content }}</p>
|
||||
<pre class="pasted">{{ template.content|escape }}</pre>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -58,20 +58,20 @@ from ietf.meeting.models import Meeting, Room, Constraint, Session, ResourceAsso
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Create (or delete) a dummy meeting for test and development purposes."
|
||||
help = "Create (or delete) a meeting for test and development purposes."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--delete', dest='delete', action='store_true', help='Delete the test and development dummy meeting')
|
||||
parser.add_argument('--delete', dest='delete', action='store_true', help='Delete the test and development meeting')
|
||||
parser.add_argument('--old-conflicts', dest='old_conflicts', action='store_true',
|
||||
help='Use old conflict types ("conflict", "conflic2", "conflic3") instead of new ("chair_conflict", "tech_overlap", "key_participant")')
|
||||
parser.add_argument(
|
||||
'--start-date',
|
||||
help='Start date for the dummy meeting (yyyy-mm-dd, defaults to 2019-11-16)',
|
||||
help='Start date for the test meeting (yyyy-mm-dd, defaults to 2019-11-16)',
|
||||
type=lambda s: datetime.datetime.strptime(s, '%Y-%m-%d').date(),
|
||||
default='2019-11-16',
|
||||
)
|
||||
parser.add_argument('--tz', default='UTC',
|
||||
help='Time zone for created meeting. Defaults to UTC. Use "" to disable.')
|
||||
help='Time zone for test meeting. Defaults to UTC. Use "" to disable.')
|
||||
|
||||
def _meeting_datetime(self, day, *time_args):
|
||||
"""Generate a datetime on a meeting day"""
|
||||
|
@ -84,7 +84,7 @@ class Command(BaseCommand):
|
|||
|
||||
def handle(self, *args, **options):
|
||||
if socket.gethostname().split('.')[0] in ['core3', 'ietfa', 'ietfb', 'ietfc', ]:
|
||||
raise EnvironmentError("Refusing to create a dummy meetng on a production server")
|
||||
raise EnvironmentError("Refusing to create a test meetng on a production server")
|
||||
|
||||
opt_delete = options.get('delete', False)
|
||||
opt_use_old_conflicts = options.get('old_conflicts', False)
|
||||
|
@ -93,9 +93,9 @@ class Command(BaseCommand):
|
|||
if opt_delete:
|
||||
if Meeting.objects.filter(number='999').exists():
|
||||
Meeting.objects.filter(number='999').delete()
|
||||
self.stdout.write("Deleted dummy meeting IETF 999 and its related objects.")
|
||||
self.stdout.write("Deleted test meeting IETF 999 and its related objects.")
|
||||
else:
|
||||
self.stderr.write("Dummy meeting IETF 999 does not exist; nothing to do.\n")
|
||||
self.stderr.write("Test meeting IETF 999 does not exist; nothing to do.\n")
|
||||
else:
|
||||
try:
|
||||
self.meeting_tz = pytz.timezone(meeting_tzname)
|
||||
|
@ -103,12 +103,12 @@ class Command(BaseCommand):
|
|||
raise CommandError("{} is not a recognized time zone.".format(meeting_tzname))
|
||||
|
||||
if Meeting.objects.filter(number='999').exists():
|
||||
self.stderr.write("Dummy meeting IETF 999 already exists; nothing to do.\n")
|
||||
self.stderr.write("Test meeting IETF 999 already exists; nothing to do.\n")
|
||||
else:
|
||||
transaction.set_autocommit(False)
|
||||
|
||||
if self.start_date.isoweekday() != 6:
|
||||
self.stderr.write("Warning: dummy meeting does not start on Saturday, watch out for bugs")
|
||||
self.stderr.write("Warning: test meeting does not start on Saturday, watch out for bugs")
|
||||
|
||||
m = Meeting.objects.create(
|
||||
number='999',
|
|
@ -674,7 +674,7 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
|||
# timeslot structure will be neighbors. The grouping algorithm relies on this!
|
||||
room_data[room.pk]['start_and_duration'],
|
||||
# Within each group, sort higher capacity rooms first.
|
||||
room.capacity,
|
||||
-room.capacity if room.capacity is not None else 1, # sort rooms with capacity = None at end
|
||||
# Finally, sort alphabetically by name
|
||||
room.name
|
||||
)
|
||||
|
@ -3486,8 +3486,12 @@ def upcoming(request):
|
|||
|
||||
entries = list(ietf_meetings)
|
||||
entries.extend(list(interim_sessions))
|
||||
entries.sort(key = lambda o: pytz.utc.localize(datetime.datetime.combine(o.date, datetime.datetime.min.time())) if isinstance(o,Meeting) else o.official_timeslotassignment().timeslot.utc_start_time())
|
||||
|
||||
entries.sort(
|
||||
key=lambda o: (
|
||||
pytz.utc.localize(datetime.datetime.combine(o.date, datetime.datetime.min.time())) if isinstance(o, Meeting) else o.official_timeslotassignment().timeslot.utc_start_time(),
|
||||
o.number if isinstance(o, Meeting) else o.meeting.number,
|
||||
)
|
||||
)
|
||||
for o in entries:
|
||||
if isinstance(o, Meeting):
|
||||
o.start_timestamp = int(pytz.utc.localize(datetime.datetime.combine(o.date, datetime.time.min)).timestamp())
|
||||
|
@ -3550,7 +3554,7 @@ def upcoming_ical(request):
|
|||
session__in=[s.pk for m in meetings for s in m.sessions if m.type_id != 'ietf'],
|
||||
timeslot__time__gte=today,
|
||||
).order_by(
|
||||
'schedule__meeting__date', 'session__type', 'timeslot__time'
|
||||
'schedule__meeting__date', 'session__type', 'timeslot__time', 'schedule__meeting__number',
|
||||
).select_related(
|
||||
'session__group', 'session__group__parent', 'timeslot', 'schedule', 'schedule__meeting'
|
||||
).distinct())
|
||||
|
|
|
@ -14,47 +14,47 @@ from ietf.group.models import Group
|
|||
from ietf.person.models import User
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = ("Create (or delete) a dummy nomcom for test and development purposes.")
|
||||
help = ("Create (or delete) a nomcom for test and development purposes.")
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--delete', dest='delete', action='store_true', help='Delete the test and development dummy nomcom')
|
||||
parser.add_argument('--delete', dest='delete', action='store_true', help='Delete the test and development nomcom')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if socket.gethostname().split('.')[0] in ['core3', 'ietfa', 'ietfb', 'ietfc', ]:
|
||||
raise EnvironmentError("Refusing to create a dummy nomcom on a production server")
|
||||
raise EnvironmentError("Refusing to create a test nomcom on a production server")
|
||||
|
||||
opt_delete = options.get('delete', False)
|
||||
if opt_delete:
|
||||
if Group.objects.filter(acronym='nomcom7437').exists():
|
||||
Group.objects.filter(acronym='nomcom7437').delete()
|
||||
User.objects.filter(username__in=['dummychair','dummymember','dummycandidate']).delete()
|
||||
self.stdout.write("Deleted dummy group 'nomcom7437' and its related objects.")
|
||||
User.objects.filter(username__in=['testchair','testmember','testcandidate']).delete()
|
||||
self.stdout.write("Deleted test group 'nomcom7437' and its related objects.")
|
||||
else:
|
||||
self.stderr.write("Dummy nomcom 'nomcom7437' does not exist; nothing to do.\n")
|
||||
self.stderr.write("test nomcom 'nomcom7437' does not exist; nothing to do.\n")
|
||||
else:
|
||||
if Group.objects.filter(acronym='nomcom7437').exists():
|
||||
self.stderr.write("Dummy nomcom 'nomcom7437' already exists; nothing to do.\n")
|
||||
self.stderr.write("test nomcom 'nomcom7437' already exists; nothing to do.\n")
|
||||
else:
|
||||
nc = NomComFactory.create(**nomcom_kwargs_for_year(year=7437,
|
||||
populate_personnel=False,
|
||||
populate_positions=False))
|
||||
|
||||
e = EmailFactory(person__name='Dummy Chair', address='dummychair@example.com', person__user__username='dummychair', person__default_emails=False, origin='dummychair')
|
||||
e = EmailFactory(person__name='Test Chair', address='testchair@example.com', person__user__username='testchair', person__default_emails=False, origin='testchair')
|
||||
e.person.user.set_password('password')
|
||||
e.person.user.save()
|
||||
nc.group.role_set.create(name_id='chair',person=e.person,email=e)
|
||||
|
||||
e = EmailFactory(person__name='Dummy Member', address='dummymember@example.com', person__user__username='dummymember', person__default_emails=False, origin='dummymember')
|
||||
e = EmailFactory(person__name='Test Member', address='testmember@example.com', person__user__username='testmember', person__default_emails=False, origin='testmember')
|
||||
e.person.user.set_password('password')
|
||||
e.person.user.save()
|
||||
nc.group.role_set.create(name_id='member',person=e.person,email=e)
|
||||
|
||||
|
||||
e = EmailFactory(person__name='Dummy Candidate', address='dummycandidate@example.com', person__user__username='dummycandidate', person__default_emails=False, origin='dummycandidate')
|
||||
e = EmailFactory(person__name='Test Candidate', address='testcandidate@example.com', person__user__username='testcandidate', person__default_emails=False, origin='testcandidate')
|
||||
e.person.user.set_password('password')
|
||||
e.person.user.save()
|
||||
NomineePositionFactory(nominee__nomcom=nc, nominee__person=e.person,
|
||||
position__nomcom=nc, position__name='Dummy Area Director', position__is_iesg_position=True,
|
||||
position__nomcom=nc, position__name='Test Area Director', position__is_iesg_position=True,
|
||||
)
|
||||
|
||||
self.stdout.write("%s\n" % key)
|
|
@ -64,9 +64,34 @@ body {
|
|||
color: #999;
|
||||
z-index: 2000000000;
|
||||
}
|
||||
|
||||
#app-loading-footer {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000000000;
|
||||
}
|
||||
|
||||
#app-loading-footer > a {
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
font-size: .9em;
|
||||
color: #0aa2c0;
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
background-color: #F9F9F9;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<div id="app"></div>
|
||||
<div id="app-loading"></div>
|
||||
<div id="app-loading">
|
||||
<div id="app-loading-footer">
|
||||
<a href="/meeting/{{ meetingData.meetingNumber }}/agenda.txt">Switch to text-only version ⮞</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -78,9 +78,9 @@
|
|||
<dt class="col-sm-2">
|
||||
Feedback
|
||||
</dt>
|
||||
<dd class="col-sm-10 pasted">
|
||||
<dd class="col-sm-10 pasted"><pre>
|
||||
{% decrypt feedback.comments request year 1 %}
|
||||
</dd>
|
||||
</pre></dd>
|
||||
</dl>
|
||||
{% if not forloop.last %}<hr>{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
{% endfor %}
|
||||
<button class="btn btn-primary"
|
||||
type="submit"
|
||||
name="end">Save feedback</button>
|
||||
name="end" value="1">Save feedback</button>
|
||||
<a class="btn btn-secondary float-end"
|
||||
href="{% url 'ietf.nomcom.views.view_feedback_pending' year %}">Back</a>
|
||||
{% else %}
|
||||
|
|
|
@ -41,9 +41,9 @@
|
|||
<dt class="col-sm-2">
|
||||
Feedback
|
||||
</dt>
|
||||
<dd class="col-sm-10 pasted">
|
||||
<dd class="col-sm-10 pasted"><pre>
|
||||
{% decrypt feedback.comments request year 1 %}
|
||||
</dd>
|
||||
</pre></dd>
|
||||
</dl>
|
||||
{% if not forloop.last %}<hr>{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
@ -42,9 +42,9 @@
|
|||
<dt class="col-sm-2">
|
||||
Feedback
|
||||
</dt>
|
||||
<dd class="col-sm-10 pasted">
|
||||
<dd class="col-sm-10 pasted"><pre>
|
||||
{% decrypt feedback.comments request year 1 %}
|
||||
</dd>
|
||||
</pre></dd>
|
||||
</dl>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue