django-smartbase-admin 1.0.6b2__py3-none-any.whl → 1.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. django_smartbase_admin/actions/admin_action_list.py +4 -1
  2. django_smartbase_admin/admin/admin_base.py +35 -32
  3. django_smartbase_admin/admin/widgets.py +1 -1
  4. django_smartbase_admin/engine/admin_base_view.py +4 -1
  5. django_smartbase_admin/engine/const.py +1 -0
  6. django_smartbase_admin/engine/dashboard.py +10 -5
  7. django_smartbase_admin/engine/filter_widgets.py +26 -1
  8. django_smartbase_admin/static/sb_admin/build/webpack.common.js +2 -0
  9. django_smartbase_admin/static/sb_admin/dist/calendar.js +1 -0
  10. django_smartbase_admin/static/sb_admin/dist/calendar_style.css +1 -0
  11. django_smartbase_admin/static/sb_admin/dist/calendar_style.js +0 -0
  12. django_smartbase_admin/static/sb_admin/dist/chart.js +1 -1
  13. django_smartbase_admin/static/sb_admin/dist/main.js +1 -1
  14. django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
  15. django_smartbase_admin/static/sb_admin/dist/table.js +1 -1
  16. django_smartbase_admin/static/sb_admin/js/fullcalendar.min.js +14804 -0
  17. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Phone-telephone.svg +3 -0
  18. django_smartbase_admin/static/sb_admin/src/css/_colors.css +19 -19
  19. django_smartbase_admin/static/sb_admin/src/css/calendar.css +130 -0
  20. django_smartbase_admin/static/sb_admin/src/css/components/_button.css +34 -0
  21. django_smartbase_admin/static/sb_admin/src/css/components/_tooltip.css +8 -22
  22. django_smartbase_admin/static/sb_admin/src/js/calendar.js +56 -0
  23. django_smartbase_admin/static/sb_admin/src/js/chart.js +3 -18
  24. django_smartbase_admin/static/sb_admin/src/js/main.js +1 -1
  25. django_smartbase_admin/static/sb_admin/src/js/multiselect.js +39 -23
  26. django_smartbase_admin/static/sb_admin/src/js/utils.js +3 -0
  27. django_smartbase_admin/templates/sb_admin/actions/change_form.html +5 -5
  28. django_smartbase_admin/templates/sb_admin/dashboard/calendar_widget.html +35 -0
  29. django_smartbase_admin/templates/sb_admin/dashboard/chart_widget.html +19 -1
  30. django_smartbase_admin/templates/sb_admin/filter_widgets/multiple_choice_field.html +14 -0
  31. django_smartbase_admin/templates/sb_admin/inlines/table_inline.html +1 -1
  32. django_smartbase_admin/templates/sb_admin/sprites/sb_admin.svg +1 -1
  33. django_smartbase_admin/templates/sb_admin/tailwind_whitelist.html +5 -3
  34. {django_smartbase_admin-1.0.6b2.dist-info → django_smartbase_admin-1.0.7.dist-info}/METADATA +1 -1
  35. {django_smartbase_admin-1.0.6b2.dist-info → django_smartbase_admin-1.0.7.dist-info}/RECORD +37 -29
  36. {django_smartbase_admin-1.0.6b2.dist-info → django_smartbase_admin-1.0.7.dist-info}/LICENSE.md +0 -0
  37. {django_smartbase_admin-1.0.6b2.dist-info → django_smartbase_admin-1.0.7.dist-info}/WHEEL +0 -0
@@ -35,6 +35,7 @@ from django_smartbase_admin.engine.const import (
35
35
  TABLE_PARAMS_SELECTED_FILTER_TYPE,
36
36
  FilterVersions,
37
37
  ADVANCED_FILTER_DATA_NAME,
38
+ IGNORE_LIST_SELECTION,
38
39
  )
39
40
  from django_smartbase_admin.services.views import SBAdminViewService
40
41
  from django_smartbase_admin.utils import import_with_injection
@@ -462,7 +463,9 @@ class SBAdminListAction(SBAdminAction):
462
463
  f'{self.view.get_menu_label()}__{timezone.now().strftime("%Y-%m-%d")}.xlsx'
463
464
  )
464
465
  columns = self.get_excel_columns()
465
- additional_filter = self.get_selection_queryset()
466
+ additional_filter = Q()
467
+ if request.request_data.modifier != IGNORE_LIST_SELECTION:
468
+ additional_filter = self.get_selection_queryset()
466
469
  data_list = []
467
470
  report_data = self.get_data(
468
471
  page_size=page_size,
@@ -195,7 +195,7 @@ class SBAdminFormFieldWidgetsMixin:
195
195
  )
196
196
 
197
197
  def get_autocomplete_widget(
198
- self, request, form_field, db_field, model, multiselect=False
198
+ self, request, form_field, db_field, model, multiselect=False
199
199
  ):
200
200
  return request.request_data.configuration.get_autocomplete_widget(
201
201
  self, request, form_field, db_field, model, multiselect
@@ -286,7 +286,7 @@ class SBAdminFormFieldWidgetsMixin:
286
286
  )
287
287
  form_field.widget = form_field_widget_instance
288
288
  if form_field.help_text == _(
289
- "Hold down “Control”, or “Command” on a Mac, to select more than one."
289
+ "Hold down “Control”, or “Command” on a Mac, to select more than one."
290
290
  ):
291
291
  form_field.help_text = ""
292
292
  return form_field
@@ -367,25 +367,25 @@ if parler_enabled:
367
367
  for translations_model in form_model._parler_meta.get_all_models():
368
368
  fields = getattr(form_new_meta, "fields", form_meta.fields)
369
369
  exclude = (
370
- getattr(form_new_meta, "exclude", form_meta.exclude) or ()
370
+ getattr(form_new_meta, "exclude", form_meta.exclude) or ()
371
371
  )
372
372
  widgets = (
373
- getattr(form_new_meta, "widgets", form_meta.widgets) or ()
373
+ getattr(form_new_meta, "widgets", form_meta.widgets) or ()
374
374
  )
375
375
  labels = (
376
- getattr(form_new_meta, "labels", form_meta.labels) or ()
376
+ getattr(form_new_meta, "labels", form_meta.labels) or ()
377
377
  )
378
378
  help_texts = (
379
- getattr(form_new_meta, "help_texts", form_meta.help_texts)
380
- or ()
379
+ getattr(form_new_meta, "help_texts", form_meta.help_texts)
380
+ or ()
381
381
  )
382
382
  error_messages = (
383
- getattr(
384
- form_new_meta,
385
- "error_messages",
386
- form_meta.error_messages,
387
- )
388
- or ()
383
+ getattr(
384
+ form_new_meta,
385
+ "error_messages",
386
+ form_meta.error_messages,
387
+ )
388
+ or ()
389
389
  )
390
390
  formfield_callback = attrs.get("formfield_callback", None)
391
391
 
@@ -406,10 +406,10 @@ if parler_enabled:
406
406
  # The next code holds the same logic as fields_for_model()
407
407
  # The f.editable check happens in _get_model_form_field()
408
408
  elif (
409
- f_name not in form_base_fields
410
- and (fields is None or f_name in fields)
411
- and f_name not in exclude
412
- and not f_name in attrs
409
+ f_name not in form_base_fields
410
+ and (fields is None or f_name in fields)
411
+ and f_name not in exclude
412
+ and not f_name in attrs
413
413
  ):
414
414
  # Get declared widget kwargs
415
415
  if f_name in widgets:
@@ -430,7 +430,7 @@ if parler_enabled:
430
430
  # See if this formfield was previously defined using a TranslatedField placeholder.
431
431
  placeholder = _get_mro_attribute(bases, f_name)
432
432
  if placeholder and isinstance(
433
- placeholder, TranslatedField
433
+ placeholder, TranslatedField
434
434
  ):
435
435
  kwargs.update(placeholder.kwargs)
436
436
 
@@ -463,7 +463,6 @@ if parler_enabled:
463
463
  mcs.parler_orig__new__(mcs, name, bases, attrs)
464
464
  return super().__new__(mcs, name, bases, attrs)
465
465
 
466
-
467
466
  class SBTranslatableModelForm(
468
467
  BaseTranslatableModelForm,
469
468
  SBAdminBaseForm,
@@ -514,7 +513,7 @@ class SBAdminInlineAndAdminCommon(SBAdminFormFieldWidgetsMixin):
514
513
  def init_view_dynamic(self, request, request_data=None, **kwargs) -> None:
515
514
  if SBAdminTranslationsService.is_translated_model(self.model):
516
515
  has_default_form = (
517
- self.form == TranslatableModelForm or self.form == forms.ModelForm
516
+ self.form == TranslatableModelForm or self.form == forms.ModelForm
518
517
  )
519
518
  if not self.form or has_default_form:
520
519
  self.form = SBTranslatableModelForm
@@ -568,12 +567,12 @@ class SBAdminThirdParty(SBAdminInlineAndAdminCommon, SBAdminBaseView):
568
567
 
569
568
  class SBAdminTranslationStatusMixin:
570
569
  def sbadmin_translation_status_row_context(
571
- self,
572
- language,
573
- languages_count,
574
- main_language_code,
575
- current_lang_code,
576
- translations_edit_url,
570
+ self,
571
+ language,
572
+ languages_count,
573
+ main_language_code,
574
+ current_lang_code,
575
+ translations_edit_url,
577
576
  ) -> dict[str, Any]:
578
577
  language_code = language[0]
579
578
  language_title = language[1]
@@ -665,7 +664,9 @@ class SBAdminInlineFormSetMixin:
665
664
  if view and view.parent_model and view.opts:
666
665
  parent_opts = view.parent_model._meta
667
666
  opts = view.opts
668
- modal_prefix = "modal__" if is_modal(SBAdminThreadLocalService.get_request()) else ""
667
+ modal_prefix = (
668
+ "modal_" if is_modal(SBAdminThreadLocalService.get_request()) else ""
669
+ )
669
670
  return f"{modal_prefix}{parent_opts.app_label}_{parent_opts.model_name}_{opts.app_label}-{opts.model_name}"
670
671
 
671
672
  return super().get_default_prefix()
@@ -723,7 +724,7 @@ class SBAdmin(
723
724
  return self.get_model_path()
724
725
 
725
726
  def get_sbadmin_fieldsets(
726
- self, request, object_id=None
727
+ self, request, object_id=None
727
728
  ) -> Iterable[tuple[str | None, dict[str, Any]]]:
728
729
  fieldsets = self.sbadmin_fieldsets or self.fieldsets
729
730
  if fieldsets:
@@ -737,7 +738,7 @@ class SBAdmin(
737
738
  self.get_form(request)()
738
739
 
739
740
  def get_fieldsets(
740
- self, request, obj=None
741
+ self, request, obj=None
741
742
  ) -> list[tuple[str | None, dict[str, Any]]]:
742
743
  fieldsets = []
743
744
  object_id = obj.id if obj else None
@@ -754,7 +755,7 @@ class SBAdmin(
754
755
  return fieldsets
755
756
 
756
757
  def get_fieldsets_context(
757
- self, request, object_id
758
+ self, request, object_id
758
759
  ) -> dict[str, dict[str | None, dict[str, Any]]]:
759
760
  fielsets_context = {}
760
761
  for fieldset in self.get_sbadmin_fieldsets(request, object_id):
@@ -970,7 +971,9 @@ class SBAdmin(
970
971
  parent_pk = request.POST.get(SBADMIN_PARENT_INSTANCE_PK_VAR)
971
972
 
972
973
  if parent_model_path and parent_pk:
973
- app_label, model_name, field, parent_model = parent_model_path.split("_", 4)
974
+ prefix, app_label, model_name, field, parent_model = (
975
+ parent_model_path.split("_", 5)
976
+ )
974
977
  content_type = ContentType.objects.get(
975
978
  app_label=app_label, model=parent_model
976
979
  )
@@ -1041,7 +1044,7 @@ class SBAdminInline(
1041
1044
 
1042
1045
  def get_context_data(self, request) -> dict[str, Any]:
1043
1046
  is_sortable_active: bool = self.sortable_field_name and (
1044
- self.has_add_permission(request) or self.has_change_permission(request)
1047
+ self.has_add_permission(request) or self.has_change_permission(request)
1045
1048
  )
1046
1049
  add_url = None
1047
1050
  try:
@@ -70,7 +70,7 @@ class SBAdminBaseWidget(ContextMixin):
70
70
  modal_prefix = ""
71
71
  try:
72
72
  modal_prefix = (
73
- "modal__" if is_modal(SBAdminThreadLocalService.get_request()) else ""
73
+ "modal_" if is_modal(SBAdminThreadLocalService.get_request()) else ""
74
74
  )
75
75
  except:
76
76
  pass
@@ -33,7 +33,8 @@ from django_smartbase_admin.engine.const import (
33
33
  BASE_PARAMS_NAME,
34
34
  TABLE_RELOAD_DATA_EVENT_NAME,
35
35
  TABLE_UPDATE_ROW_DATA_EVENT_NAME,
36
- FILTER_DATA_NAME,
36
+ SELECT_ALL_KEYWORD,
37
+ IGNORE_LIST_SELECTION,
37
38
  )
38
39
  from django_smartbase_admin.services.views import SBAdminViewService
39
40
  from django_smartbase_admin.services.xlsx_export import (
@@ -223,6 +224,7 @@ class SBAdminBaseView(object):
223
224
  "GLOBAL_FILTER_ALIAS_WIDGET_ID": GLOBAL_FILTER_ALIAS_WIDGET_ID,
224
225
  "TABLE_RELOAD_DATA_EVENT_NAME": TABLE_RELOAD_DATA_EVENT_NAME,
225
226
  "TABLE_UPDATE_ROW_DATA_EVENT_NAME": TABLE_UPDATE_ROW_DATA_EVENT_NAME,
227
+ "SELECT_ALL_KEYWORD": SELECT_ALL_KEYWORD,
226
228
  }
227
229
  ),
228
230
  }
@@ -489,6 +491,7 @@ class SBAdminBaseListView(SBAdminBaseView):
489
491
  title=_("Download XLSX"),
490
492
  view=self,
491
493
  action_id=Action.XLSX_EXPORT.value,
494
+ action_modifier=IGNORE_LIST_SELECTION,
492
495
  )
493
496
  ]
494
497
  return self.sbadmin_list_actions
@@ -31,6 +31,7 @@ DEFAULT_PAGE_SIZE = 20
31
31
  PAGE_SIZE_OPTIONS = [10, 20, 50, 100]
32
32
  AUTOCOMPLETE_PAGE_SIZE = 20
33
33
  XLSX_PAGE_CHUNK_SIZE = 50000
34
+ IGNORE_LIST_SELECTION = "__all__"
34
35
  NEW_OBJECT_ID = 0
35
36
  OBJECT_ID_PLACEHOLDER = -1
36
37
  ALL_MODEL_FIELDS = "__all__"
@@ -1,5 +1,4 @@
1
- import pickle
2
- from copy import copy, deepcopy
1
+ from copy import copy
3
2
  from datetime import timedelta
4
3
 
5
4
  from django.core.cache import cache
@@ -17,10 +16,8 @@ from django_smartbase_admin.engine.admin_view import SBAdminView
17
16
  from django_smartbase_admin.engine.const import OBJECT_ID_PLACEHOLDER
18
17
  from django_smartbase_admin.engine.field import SBAdminField
19
18
  from django_smartbase_admin.engine.filter_widgets import (
20
- ChoiceFilterWidget,
21
19
  DateFilterWidget,
22
- RadioChoiceFilterWidget,
23
- )
20
+ RadioChoiceFilterWidget, )
24
21
  from django_smartbase_admin.services.views import SBAdminViewService
25
22
  from django_smartbase_admin.utils import to_list
26
23
 
@@ -666,5 +663,13 @@ class SBAdminDashboardListWidget(SBAdminBaseListView, SBAdminDashboardWidget):
666
663
  "viewsModule",
667
664
  "tableParamsModule",
668
665
  "detailViewModule",
666
+ "filterModule",
669
667
  ]
670
668
  return tabulator_definition
669
+
670
+
671
+ class SbAdminCalendarWidget(SBAdminDashboardWidget):
672
+ template_name = "sb_admin/dashboard/calendar_widget.html"
673
+
674
+ def action_get_data(self, request, modifier):
675
+ return JsonResponse(data=self.get_cached_data(request), safe=False)
@@ -19,7 +19,7 @@ from django_smartbase_admin.engine.const import (
19
19
  AUTOCOMPLETE_PAGE_SIZE,
20
20
  Action,
21
21
  AUTOCOMPLETE_PAGE_NUM,
22
- AUTOCOMPLETE_FORWARD_NAME,
22
+ AUTOCOMPLETE_FORWARD_NAME, SELECT_ALL_KEYWORD,
23
23
  )
24
24
  from django_smartbase_admin.services.translations import SBAdminTranslationsService
25
25
  from django_smartbase_admin.services.views import SBAdminViewService
@@ -220,6 +220,31 @@ class RadioChoiceFilterWidget(ChoiceFilterWidget):
220
220
 
221
221
  class MultipleChoiceFilterWidget(AutocompleteParseMixin, ChoiceFilterWidget):
222
222
  template_name = "sb_admin/filter_widgets/multiple_choice_field.html"
223
+ enable_select_all = False
224
+ select_all_keyword = None
225
+ select_all_label = None
226
+
227
+ def __init__(
228
+ self,
229
+ choices,
230
+ template_name=None,
231
+ default_value=None,
232
+ default_label=None,
233
+ enable_select_all=False,
234
+ select_all_keyword=SELECT_ALL_KEYWORD,
235
+ select_all_label=_('All'),
236
+ **kwargs,
237
+ ) -> None:
238
+ super().__init__(
239
+ choices=choices,
240
+ template_name=template_name,
241
+ default_value=default_value,
242
+ default_label=default_label,
243
+ **kwargs,
244
+ )
245
+ self.enable_select_all = enable_select_all
246
+ self.select_all_keyword = select_all_keyword
247
+ self.select_all_label = select_all_label
223
248
 
224
249
  def get_base_filter_query_for_parsed_value(self, request, filter_value):
225
250
  return Q(**{f"{self.field.filter_field}__in": filter_value})
@@ -7,11 +7,13 @@ const entries = {
7
7
  main: './src/django_smartbase_admin/static/sb_admin/src/js/main.js',
8
8
  table: './src/django_smartbase_admin/static/sb_admin/src/js/table.js',
9
9
  chart: './src/django_smartbase_admin/static/sb_admin/src/js/chart.js',
10
+ calendar: './src/django_smartbase_admin/static/sb_admin/src/js/calendar.js',
10
11
  main_style: './src/django_smartbase_admin/static/sb_admin/src/css/style.css',
11
12
  translations: './src/django_smartbase_admin/static/sb_admin/src/js/translations.js',
12
13
  confirmation_modal: './src/django_smartbase_admin/static/sb_admin/src/js/confirmation_modal.js',
13
14
  tree_widget: './src/django_smartbase_admin/static/sb_admin/src/js/tree_widget.js',
14
15
  tree_widget_style: './src/django_smartbase_admin/static/sb_admin/src/css/tree_widget.css',
16
+ calendar_style: './src/django_smartbase_admin/static/sb_admin/src/css/calendar.css',
15
17
  }
16
18
 
17
19
  const projectRoot = process.env.PWD || process.cwd()
@@ -0,0 +1 @@
1
+ (()=>{"use strict";const e=e=>{const t=e.dataset.filterId||e.id,n=e.dataset.labelSeparator||", ",s=document.querySelector(`#${t}-value`);if(!s)return;const r=e.dataset.label;if(r)return s.innerHTML=r,s;const i=(e=>{try{return JSON.parse(e)}catch(e){}return e})(e.value);if(""===e.value||"[]"===e.value)return e.dataset.emptyLabel?s.innerHTML=e.dataset.emptyLabel:s.innerHTML="",s;if("object"==typeof i){const e=[],t=Object.values(i);let r=!1;for(let[n,s]of t.entries()){if(n===window.sb_admin_const.MULTISELECT_FILTER_MAX_CHOICES_SHOWN)break;if(!(t.length>1&&s.value===window.sb_admin_const.SELECT_ALL_KEYWORD)){if(n===window.sb_admin_const.MULTISELECT_FILTER_MAX_CHOICES_SHOWN-2&&t[n+2]){e.push(s.label),r=!0;break}e.push(s.label)}}let a=e.join(n);r&&(a=a.substring(0,a.length),a+="... +"+(t.length-window.sb_admin_const.MULTISELECT_FILTER_MAX_CHOICES_SHOWN+1)),s.innerHTML=a}else try{s.innerHTML=e.options[e.selectedIndex].text}catch(t){const n=document.querySelector(`label[for=${e.id}]`);if(n)s.innerHTML=n.innerText;else{let t;try{t=document.querySelector(`label[for=${e.id}_${e.value}]`)}catch(e){t=null}s.innerHTML=t?t.innerText:i}}return s};window.SBAdminCalendarClass=class{constructor(e){if(this.options=e,this.calendar=null,this.options.calendarOptions=this.options.calendarOptions||{},!Object.hasOwn(this.options.calendarOptions,"events")||!this.options.calendarOptions.events)throw Error("Missing events property!");"string"==typeof this.options.calendarOptions.events&&(this.options.calendarOptions.events={url:this.options.calendarOptions.events}),this.options.calendarOptions.events.extraParams=this.options.calendarOptions.events.extraParams||{},this.options.calendarOptions.events.extraParams=()=>({...this.options.calendarOptions.events.extraParams||{},...this.getFilterData()}),this.initCalendar(),this.initFilters()}initCalendar(){const e=document.getElementById(`${this.options.widgetId}-calendar`);this.calendar=new FullCalendar.Calendar(e,this.options.calendarOptions||{}),this.calendar.render()}getFilterData(){const e=document.querySelector(`#${this.options.widgetId}-filter-form`),t=new FormData(e).entries(),n={};for(const[e,s]of t)s&&(n[e]=s);return n}initFilters(){var t,n;t=`[form="${this.options.widgetId}-filter-form"]`,n=t=>{this.calendar.refetchEvents(),e(t.target)},document.querySelectorAll(t).forEach((e=>{e.addEventListener("change",n),e.addEventListener("keypress",(t=>{if(13===t.keyCode)return e.blur(),t.preventDefault(),e.focus(),!0})),e.addEventListener("SBAutocompleteChange",n)}))}}})();
@@ -0,0 +1 @@
1
+ :root{--fc-small-font-size:.75rem;--fc-border-color:var(--color-dark-200);--fc-today-bg-color:var(--color-primary-50);--fc-neutral-bg-color:var(--color-dark-100)}.fc-theme-standard th.fc-col-header-cell{background-color:var(--color-dark-100);padding:.5rem 1.5rem}.fc-theme-standard td.fc-daygrid-day{padding:.5rem}.fc table{font-size:.75rem;line-height:1.25rem}.fc .fc-col-header-cell-cushion{font-size:.875rem;font-weight:500;line-height:1.25rem;padding:0}.fc .fc-daygrid-day{height:9rem}.fc .fc-day-other{background-color:var(--color-dark-100)}.fc .fc-day-other .fc-daygrid-day-top{opacity:1}.fc .fc-day-other .fc-daygrid-day-top .fc-daygrid-day-number{color:var(--color-dark)}.fc .fc-day-other .fc-daygrid-day-events{opacity:.6}.fc .fc-daygrid-day-bottom:after,.fc .fc-daygrid-day-bottom:before,.fc .fc-daygrid-day-events:after,.fc .fc-daygrid-day-events:before,.fc .fc-daygrid-day-frame:after,.fc .fc-daygrid-day-frame:before,.fc .fc-daygrid-event-harness:after,.fc .fc-daygrid-event-harness:before{display:none}.fc .fc-daygrid-day-events,.fc .fc-daygrid-day-frame{display:flex;flex-direction:column;gap:.25rem}.fc .fc-daygrid-day-events{margin:0}.fc .fc-daygrid-day-top{flex-direction:row}.fc .fc-daygrid-day-number{align-items:center;color:var(--color-dark-900);display:flex;font-weight:600;height:1.5rem;justify-content:center;line-height:1;padding:.25rem;width:1.5rem}.fc .fc-day-today .fc-daygrid-day-number{background-color:var(--color-primary);border-radius:9999px;color:var(--color-light)}.fc .fc-daygrid-event{display:flex;gap:.25rem;margin:0!important}.fc .fc-daygrid-dot-event{border-color:var(--color-dark-200);border-radius:6px;border-width:1px;height:1.5rem;padding:0 .5rem}.fc .fc-daygrid-dot-event .fc-event-time{display:none}.fc .fc-daygrid-dot-event .fc-event-title{color:var(--color-dark-900);font-weight:400;line-height:1rem;text-overflow:ellipsis}.fc .fc-daygrid-event-dot{border-color:currentColor;border-radius:9999px;border-width:2px;box-sizing:border-box;flex-shrink:0;height:.75rem;margin:0;width:.75rem}.fc .fc-daygrid-day-bottom{font-size:.75rem;line-height:1.25rem;margin:0!important}.fc .fc-daygrid-more-link{margin:0;padding:0}.fc .fc-daygrid-more-link:hover{background:none}.fc .fc-daygrid-body-unbalanced .fc-daygrid-day-events{margin-bottom:0;min-height:4rem}.fc .fc-daygrid-day-bg{display:none}.fc .fc-popover{z-index:10}.fc .fc-popover .fc-popover-header{padding:.25rem}.fc .fc-popover .fc-popover-title{font-size:.875rem;line-height:1.25rem}.fc .fc-popover .fc-popover-body{display:flex;flex-direction:column;gap:.25rem;padding:.5rem}.fc .fc-scrollgrid{border-left-width:0}.fc .fc-scrollgrid-section>td,.fc .fc-scrollgrid-section>th{border:0}