datalog-theme 0.6.0

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 (180) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +162 -0
  3. data/CITATION.cff +29 -0
  4. data/LICENSE +21 -0
  5. data/README.md +257 -0
  6. data/_data/academic.yml +217 -0
  7. data/_data/cdn-integrity.yml +51 -0
  8. data/_data/config/author.yml +121 -0
  9. data/_data/config/features.yml +262 -0
  10. data/_data/config/site.yml +42 -0
  11. data/_data/config/theme.yml +181 -0
  12. data/_data/datasets.yml +28 -0
  13. data/_data/i18n/en.yml +438 -0
  14. data/_data/i18n/es.yml +438 -0
  15. data/_data/i18n/pt.yml +438 -0
  16. data/_data/js_manifest.json +22 -0
  17. data/_data/js_meta.json +371 -0
  18. data/_data/navigation.yml +145 -0
  19. data/_data/projects.yml +41 -0
  20. data/_data/publications.yml +28 -0
  21. data/_data/social.yml +73 -0
  22. data/_data/visualizations.yml +51 -0
  23. data/_includes/analytics/dashboard.html +149 -0
  24. data/_includes/analytics.html +9 -0
  25. data/_includes/components/academic-dashboard.html +347 -0
  26. data/_includes/components/advanced-search.html +682 -0
  27. data/_includes/components/api-function.html +94 -0
  28. data/_includes/components/author-bio.html +166 -0
  29. data/_includes/components/bookmark-system.html +96 -0
  30. data/_includes/components/breadcrumbs.html +89 -0
  31. data/_includes/components/citation-tools.html +94 -0
  32. data/_includes/components/comments.html +244 -0
  33. data/_includes/components/content-provenance.html +43 -0
  34. data/_includes/components/content-recommendations.html +228 -0
  35. data/_includes/components/difficulty-badge.html +84 -0
  36. data/_includes/components/email-preferences.html +200 -0
  37. data/_includes/components/enhanced-code-block.html +212 -0
  38. data/_includes/components/enhanced-metadata.html +228 -0
  39. data/_includes/components/enhanced-toc.html +209 -0
  40. data/_includes/components/hero.html +15 -0
  41. data/_includes/components/language-switcher.html +396 -0
  42. data/_includes/components/math-fallback.html +19 -0
  43. data/_includes/components/navigation-enhancements.html +454 -0
  44. data/_includes/components/newsletter-signup.html +178 -0
  45. data/_includes/components/open-science-badges.html +16 -0
  46. data/_includes/components/package-install.html +194 -0
  47. data/_includes/components/performance-monitor.html +170 -0
  48. data/_includes/components/popular-posts.html +233 -0
  49. data/_includes/components/post-hero.html +62 -0
  50. data/_includes/components/reading-progress.html +133 -0
  51. data/_includes/components/reading-time.html +121 -0
  52. data/_includes/components/responsive-image.html +63 -0
  53. data/_includes/components/search-facets.html +307 -0
  54. data/_includes/components/series-navigation.html +124 -0
  55. data/_includes/components/social-proof.html +34 -0
  56. data/_includes/components/social-share.html +119 -0
  57. data/_includes/components/user-preferences.html +566 -0
  58. data/_includes/components/visualization-card.html +24 -0
  59. data/_includes/components/viz-table-fallback.html +19 -0
  60. data/_includes/critical-css/default.html +0 -0
  61. data/_includes/critical-css/home.html +0 -0
  62. data/_includes/critical-css/post.html +0 -0
  63. data/_includes/csp-meta.html +17 -0
  64. data/_includes/footer/nav-column.html +31 -0
  65. data/_includes/footer.html +76 -0
  66. data/_includes/head.html +337 -0
  67. data/_includes/header/navigation.html +48 -0
  68. data/_includes/header.html +70 -0
  69. data/_includes/helpers/array-to-sentence.html +21 -0
  70. data/_includes/helpers/date-format.html +16 -0
  71. data/_includes/helpers/link-with-icon.html +26 -0
  72. data/_includes/layouts/default/article.html +67 -0
  73. data/_includes/meta/language-attributes.html +25 -0
  74. data/_includes/meta/math-config.html +21 -0
  75. data/_includes/meta/schema.html +153 -0
  76. data/_includes/meta/scripts-loader.html +52 -0
  77. data/_includes/post/related-posts.html +63 -0
  78. data/_includes/scripts.html +72 -0
  79. data/_includes/skip-link.html +1 -0
  80. data/_includes/toc.html +2 -0
  81. data/_layouts/archive.html +282 -0
  82. data/_layouts/dataset.html +52 -0
  83. data/_layouts/default.html +33 -0
  84. data/_layouts/home.html +48 -0
  85. data/_layouts/notebook.html +173 -0
  86. data/_layouts/package.html +238 -0
  87. data/_layouts/page.html +16 -0
  88. data/_layouts/portfolio.html +41 -0
  89. data/_layouts/post-sidebar.html +183 -0
  90. data/_layouts/post.html +338 -0
  91. data/_layouts/project.html +252 -0
  92. data/_layouts/research.html +504 -0
  93. data/_plugins/analytics_dashboard.rb +315 -0
  94. data/_plugins/config_validator.rb +397 -0
  95. data/_plugins/csp_generator.rb +115 -0
  96. data/_plugins/datalog_bibliography.rb +19 -0
  97. data/_plugins/datalog_comments.rb +18 -0
  98. data/_plugins/datalog_slides.rb +20 -0
  99. data/_plugins/front_matter_compat.rb +109 -0
  100. data/_plugins/i18n.rb +160 -0
  101. data/_plugins/image_optimizer.rb +445 -0
  102. data/_plugins/math_preprocessor.rb +185 -0
  103. data/_plugins/notebook_converter.rb +1107 -0
  104. data/_plugins/plugin_loader.rb +185 -0
  105. data/_plugins/publications_generator.rb +234 -0
  106. data/_plugins/reading_time.rb +15 -0
  107. data/_plugins/search_normalizer.rb +74 -0
  108. data/_plugins/sri_filter.rb +18 -0
  109. data/_plugins/toc_filter.rb +68 -0
  110. data/_plugins/warning_filter.rb +3 -0
  111. data/_sass/_base.scss +142 -0
  112. data/_sass/_components.scss +2380 -0
  113. data/_sass/_header.scss +470 -0
  114. data/_sass/_interactive.scss +344 -0
  115. data/_sass/_layout.scss +1760 -0
  116. data/_sass/_mathematical.scss +368 -0
  117. data/_sass/_mixins.scss +99 -0
  118. data/_sass/_package-docs.scss +715 -0
  119. data/_sass/_phase1-enhancements.scss +721 -0
  120. data/_sass/_phase3-enhancements.scss +874 -0
  121. data/_sass/_phase4-enhancements.scss +1214 -0
  122. data/_sass/_phase5-enhancements.scss +414 -0
  123. data/_sass/_search.scss +654 -0
  124. data/_sass/_syntax-highlighting.scss +128 -0
  125. data/_sass/_theme.scss +79 -0
  126. data/_sass/_typography.scss +148 -0
  127. data/_sass/_utilities.scss +334 -0
  128. data/_sass/_variables.scss +153 -0
  129. data/_sass/_visualizations.scss +242 -0
  130. data/assets/css/main.scss +4 -0
  131. data/assets/img/20220607123041_detail.001.png +0 -0
  132. data/assets/img/favicons/android-chrome-192x192.png +0 -0
  133. data/assets/img/favicons/android-chrome-512x512.png +0 -0
  134. data/assets/img/favicons/apple-touch-icon.png +0 -0
  135. data/assets/img/favicons/favicon-16x16.png +0 -0
  136. data/assets/img/favicons/favicon-32x32.png +0 -0
  137. data/assets/img/favicons/favicon.ico +0 -0
  138. data/assets/img/favicons/site.webmanifest +21 -0
  139. data/assets/img/portfolio-placeholder.svg +20 -0
  140. data/assets/js/academic.js +262 -0
  141. data/assets/js/analytics-dashboard.js +382 -0
  142. data/assets/js/core/dark-mode.js +79 -0
  143. data/assets/js/core/github-cards.js +123 -0
  144. data/assets/js/core/language-filter.js +69 -0
  145. data/assets/js/core/navigation.js +184 -0
  146. data/assets/js/core/scroll-progress.js +45 -0
  147. data/assets/js/core/search-hotkeys.js +62 -0
  148. data/assets/js/core/skip-links.js +62 -0
  149. data/assets/js/loader.js +163 -0
  150. data/assets/js/main.js +23 -0
  151. data/assets/js/math.js +818 -0
  152. data/assets/js/notebook.js +158 -0
  153. data/assets/js/search/analytics.js +91 -0
  154. data/assets/js/search/app.js +271 -0
  155. data/assets/js/search/autocomplete.js +120 -0
  156. data/assets/js/search/engine.js +260 -0
  157. data/assets/js/search/filters.js +38 -0
  158. data/assets/js/search/render.js +217 -0
  159. data/assets/js/search/utils.js +99 -0
  160. data/assets/js/search.js +354 -0
  161. data/assets/js/visualizations.js +816 -0
  162. data/assets/publications/datalog-publications.bib +8 -0
  163. data/assets/publications/datalog-publications.ris +9 -0
  164. data/assets/publications/publications.bib +30 -0
  165. data/assets/templates/diogo-ribeiro-cv.md +31 -0
  166. data/assets/templates/diogo-ribeiro-cv.tex +32 -0
  167. data/bin/datalog +26 -0
  168. data/datalog-theme.gemspec +72 -0
  169. data/lib/datalog/cli.rb +563 -0
  170. data/lib/datalog/plugin_system/dependency_resolver.rb +223 -0
  171. data/lib/datalog/plugin_system.rb +237 -0
  172. data/lib/datalog/plugins/citations.rb +182 -0
  173. data/lib/datalog/plugins/comments.rb +183 -0
  174. data/lib/datalog/plugins/search.rb +45 -0
  175. data/lib/datalog/plugins/slides.rb +121 -0
  176. data/lib/datalog/theme/theme.rb +18 -0
  177. data/lib/datalog/theme/version.rb +7 -0
  178. data/lib/datalog/warning_filter.rb +28 -0
  179. data/lib/datalog-theme.rb +17 -0
  180. metadata +555 -0
@@ -0,0 +1,51 @@
1
+ - title: Global CO₂ Emissions Explorer
2
+ summary: Interactive Plotly dashboard tracking CO₂ emissions across regions with scenario toggles.
3
+ tech: plotly
4
+ tags: [climate, python, dashboards]
5
+ version: 1.2
6
+ updated: 2024-02-01
7
+ demo: https://example.com/datalog/co2-dashboard/
8
+ snapshot: https://example.com/datalog/co2-dashboard/static/co2-dashboard.png
9
+ source: https://github.com/DiogoRibeiro7/datalog-co2-dashboard
10
+ - title: Streaming Sensor Scatterplot
11
+ summary: WebGL-accelerated D3 scatterplot that streams manufacturing telemetry with brushing controls.
12
+ tech: d3
13
+ tags: [manufacturing, streaming, javascript]
14
+ version: 0.9
15
+ updated: 2023-12-11
16
+ demo: https://example.com/datalog/d3-streaming/
17
+ source: https://github.com/DiogoRibeiro7/datalog-d3-streaming
18
+ - title: Observable Notebook — Topic Modeling
19
+ summary: Observable-powered topic modeling notebook with live parameter tuning and data provenance notes.
20
+ tech: observable
21
+ tags: [nlp, notebooks, reproducibility]
22
+ version: 1.0
23
+ updated: 2024-01-18
24
+ demo: https://observablehq.com/embed/@datalog/topic-modeling-overview?cells=chart
25
+ snapshot: https://example.com/datalog/topic-modeling/static/overview.pdf
26
+ source: https://observablehq.com/@datalog/topic-modeling-overview
27
+ - title: Bokeh Pipeline Performance Monitor
28
+ summary: Bokeh dashboard summarising nightly ETL run durations with alert thresholds and filters.
29
+ tech: bokeh
30
+ tags: [etl, python, operations]
31
+ version: 2.3
32
+ updated: 2024-03-05
33
+ demo: https://example.com/datalog/bokeh-pipeline/
34
+ snapshot: https://example.com/datalog/bokeh-pipeline/static/export.html
35
+ source: https://github.com/DiogoRibeiro7/datalog-bokeh-pipeline
36
+ - title: R Shiny Demand Forecaster
37
+ summary: Forecasting interface for retail demand with what-if scenarios and automated report exports.
38
+ tech: shiny
39
+ tags: [forecasting, r, retail]
40
+ version: 1.4
41
+ updated: 2024-02-22
42
+ demo: https://example.com/datalog/shiny-demand-forecast/
43
+ source: https://github.com/DiogoRibeiro7/datalog-shiny-demand
44
+ - title: Jupyter Widget Model Inspector
45
+ summary: Ipywidgets-powered model inspector for comparing feature importance distributions interactively.
46
+ tech: ipywidgets
47
+ tags: [machine-learning, explainability, python]
48
+ version: 0.6
49
+ updated: 2024-01-30
50
+ snapshot: https://example.com/datalog/widget-inspector/static/inspector.html
51
+ source: https://github.com/DiogoRibeiro7/datalog-widget-inspector
@@ -0,0 +1,149 @@
1
+ {% assign analytics = site.data.analytics_dashboard | default: {} %}
2
+ <div class="analytics-dashboard" data-status="{{ analytics.status | default: 'unavailable' }}">
3
+ <div class="analytics-header">
4
+ <div>
5
+ <h2 class="analytics-title">{% t 'analytics.dashboard.title' fallback: 'Analytics Overview' %}</h2>
6
+ <p class="analytics-subtitle">
7
+ {% if analytics.status == 'ok' %}
8
+ {% t 'analytics.dashboard.description' fallback: 'Insights powered by Google Analytics 4, refreshed every 24 hours.' %}
9
+ {% else %}
10
+ {% t 'analytics.dashboard.disabled' fallback: 'Connect Google Analytics 4 to populate this dashboard. Cached results are reused for 24 hours.' %}
11
+ {% endif %}
12
+ </p>
13
+ </div>
14
+ <div class="analytics-meta">
15
+ <span class="analytics-refresh">
16
+ {% assign fetched = analytics.fetched_at %}
17
+ {% if fetched %}
18
+ {% t 'analytics.dashboard.last_updated' fallback: 'Last updated' %}: <time datetime="{{ fetched }}">{{ fetched | date: '%Y-%m-%d %H:%M' }}</time>
19
+ {% else %}
20
+ {% t 'analytics.dashboard.last_updated_unknown' fallback: 'Last updated: pending' %}
21
+ {% endif %}
22
+ </span>
23
+ <span class="analytics-cache-window">{% t 'analytics.dashboard.cache_window' fallback: 'Data cached for 24 hours' %}</span>
24
+ </div>
25
+ </div>
26
+
27
+ {% unless analytics.status == 'ok' %}
28
+ <div class="analytics-alert">
29
+ <p>{{ analytics.message | default: "Configure GA4 credentials via GA4_PROPERTY_ID and GA4_CREDENTIALS_JSON." }}</p>
30
+ </div>
31
+ {% endunless %}
32
+
33
+ <div class="analytics-grid">
34
+ <section class="analytics-card">
35
+ <header>
36
+ <h3>{% t 'analytics.dashboard.top_posts' fallback: 'Top posts' %}</h3>
37
+ </header>
38
+ <table class="analytics-table" aria-live="polite">
39
+ <thead>
40
+ <tr>
41
+ <th scope="col">{% t 'analytics.dashboard.table.post' fallback: 'Post' %}</th>
42
+ <th scope="col">{% t 'analytics.dashboard.table.views' fallback: 'Views' %}</th>
43
+ <th scope="col">{% t 'analytics.dashboard.table.engagement' fallback: 'Engagement (s)' %}</th>
44
+ </tr>
45
+ </thead>
46
+ <tbody id="analytics-top-posts" data-empty-message="{% t 'analytics.dashboard.no_results' fallback: 'No data available' %}">
47
+ <tr class="placeholder">
48
+ <td colspan="3">{% t 'analytics.dashboard.loading' fallback: 'Loading analytics…' %}</td>
49
+ </tr>
50
+ </tbody>
51
+ </table>
52
+ </section>
53
+
54
+ <section class="analytics-card">
55
+ <header>
56
+ <h3>{% t 'analytics.dashboard.search_terms' fallback: 'Search queries' %}</h3>
57
+ </header>
58
+ <table class="analytics-table" aria-live="polite">
59
+ <thead>
60
+ <tr>
61
+ <th scope="col">{% t 'analytics.dashboard.table.query' fallback: 'Query' %}</th>
62
+ <th scope="col">{% t 'analytics.dashboard.table.sessions' fallback: 'Sessions' %}</th>
63
+ </tr>
64
+ </thead>
65
+ <tbody id="analytics-search-terms" data-empty-message="{% t 'analytics.dashboard.no_searches' fallback: 'No search queries recorded' %}">
66
+ <tr class="placeholder">
67
+ <td colspan="2">{% t 'analytics.dashboard.loading' fallback: 'Loading analytics…' %}</td>
68
+ </tr>
69
+ </tbody>
70
+ </table>
71
+ </section>
72
+
73
+ <section class="analytics-card analytics-card--wide">
74
+ <header>
75
+ <h3>{% t 'analytics.dashboard.visitor_trends' fallback: 'Visitor trends' %}</h3>
76
+ </header>
77
+ <canvas id="analytics-visitors-chart" height="240" role="img" aria-label="{% t 'analytics.dashboard.visitor_trends' fallback: 'Visitor trends' %}"></canvas>
78
+ </section>
79
+
80
+ <section class="analytics-card analytics-card--wide">
81
+ <header>
82
+ <h3>{% t 'analytics.dashboard.engagement' fallback: 'Engagement by post' %}</h3>
83
+ </header>
84
+ <canvas id="analytics-engagement-chart" height="240" role="img" aria-label="{% t 'analytics.dashboard.engagement' fallback: 'Engagement by post' %}"></canvas>
85
+ </section>
86
+
87
+ <section class="analytics-card">
88
+ <header>
89
+ <h3>{% t 'analytics.dashboard.events' fallback: 'Notebook & demo events' %}</h3>
90
+ </header>
91
+ <ul class="analytics-metric-list" id="analytics-key-events">
92
+ <li class="placeholder">{% t 'analytics.dashboard.loading' fallback: 'Loading analytics…' %}</li>
93
+ </ul>
94
+ </section>
95
+
96
+ <section class="analytics-card">
97
+ <header>
98
+ <h3>{% t 'analytics.dashboard.scholar' fallback: 'Google Scholar' %}</h3>
99
+ </header>
100
+ <dl class="analytics-definition-list">
101
+ <div>
102
+ <dt>{% t 'analytics.dashboard.scholar_total' fallback: 'Total citations' %}</dt>
103
+ <dd data-field="scholar-total">—</dd>
104
+ </div>
105
+ <div>
106
+ <dt>h-index</dt>
107
+ <dd data-field="scholar-h-index">—</dd>
108
+ </div>
109
+ <div>
110
+ <dt>i10-index</dt>
111
+ <dd data-field="scholar-i10-index">—</dd>
112
+ </div>
113
+ </dl>
114
+ <canvas id="analytics-scholar-chart" height="200" role="img" aria-label="{% t 'analytics.dashboard.scholar_trend' fallback: 'Citations by year' %}"></canvas>
115
+ </section>
116
+
117
+ <section class="analytics-card analytics-card--wide">
118
+ <header>
119
+ <h3>{% t 'analytics.dashboard.monthly_reports' fallback: 'Monthly reports' %}</h3>
120
+ </header>
121
+ <table class="analytics-table" aria-live="polite">
122
+ <thead>
123
+ <tr>
124
+ <th scope="col">{% t 'analytics.dashboard.table.month' fallback: 'Month' %}</th>
125
+ <th scope="col">{% t 'analytics.dashboard.table.users' fallback: 'Users' %}</th>
126
+ <th scope="col">{% t 'analytics.dashboard.table.new_users' fallback: 'New users' %}</th>
127
+ <th scope="col">{% t 'analytics.dashboard.table.sessions' fallback: 'Sessions' %}</th>
128
+ <th scope="col">{% t 'analytics.dashboard.table.page_views' fallback: 'Page views' %}</th>
129
+ </tr>
130
+ </thead>
131
+ <tbody id="analytics-monthly-reports" data-empty-message="{% t 'analytics.dashboard.no_reports' fallback: 'No monthly reports available' %}">
132
+ <tr class="placeholder">
133
+ <td colspan="5">{% t 'analytics.dashboard.loading' fallback: 'Loading analytics…' %}</td>
134
+ </tr>
135
+ </tbody>
136
+ </table>
137
+ </section>
138
+ </div>
139
+
140
+ <script src="{{ 'https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js' | add_sri }}" defer></script>
141
+ <script nonce="{{ page.csp_nonce }}">
142
+ {% if analytics %}
143
+ window.__DATALOG_ANALYTICS__ = {{ analytics | jsonify }};
144
+ {% else %}
145
+ window.__DATALOG_ANALYTICS__ = {};
146
+ {% endif %}
147
+ </script>
148
+ <script src="{{ '/assets/js/analytics-dashboard.js' | relative_url }}" defer></script>
149
+ </div>
@@ -0,0 +1,9 @@
1
+ {% if jekyll.environment == 'production' and site.google_analytics and site.google_analytics != '' %}
2
+ <script async src="https://www.googletagmanager.com/gtag/js?id={{ site.google_analytics }}"></script>
3
+ <script nonce="{{ page.csp_nonce }}">
4
+ window.dataLayer = window.dataLayer || [];
5
+ function gtag(){dataLayer.push(arguments);}
6
+ gtag('js', new Date());
7
+ gtag('config', '{{ site.google_analytics }}', { 'anonymize_ip': true });
8
+ </script>
9
+ {% endif %}
@@ -0,0 +1,347 @@
1
+ {% assign academic = include.academic | default: site.data.academic %}
2
+ {% assign publication_data = site.data.publications %}
3
+ {% assign citation_metrics = publication_data.metrics | default: academic.citations.metrics %}
4
+ {% assign localization_config = site.theme_options.localization %}
5
+ {% assign date_format = localization_config.date_format | default: 'long' %}
6
+ {% assign datetime_format = localization_config.datetime_format | default: 'datetime' %}
7
+ {% assign since_stats = nil %}
8
+ {% assign since_period = nil %}
9
+ {% if citation_metrics %}
10
+ {% for metric_pair in citation_metrics %}
11
+ {% assign metric_key = metric_pair[0] %}
12
+ {% if metric_key contains 'since_' %}
13
+ {% assign since_stats = metric_pair[1] %}
14
+ {% assign since_period = metric_key | remove: 'since_' %}
15
+ {% break %}
16
+ {% endif %}
17
+ {% endfor %}
18
+ {% endif %}
19
+ {% if since_period == nil or since_period == '' %}
20
+ {% assign since_period = academic.citations.reporting_period %}
21
+ {% endif %}
22
+ <section class="academic-dashboard">
23
+ <header class="academic-dashboard__header">
24
+ <h1>{% t 'academic_dashboard.header.title' %}</h1>
25
+ <p>{% t 'academic_dashboard.header.description' %}</p>
26
+ </header>
27
+
28
+ <section class="academic-dashboard__profiles" aria-labelledby="academic-profiles-heading">
29
+ <h2 id="academic-profiles-heading">{% t 'academic_dashboard.profiles.title' %}</h2>
30
+ <div class="academic-profiles__grid">
31
+ {% for key in 'google_scholar|orcid|researchgate|academia' | split: '|' %}
32
+ {% assign profile = academic.profiles[key] %}
33
+ {% if profile %}
34
+ <article class="academic-profile" data-profile="{{ key }}">
35
+ <header>
36
+ <h3>{{ profile.label | default: key | replace: '_', ' ' | capitalize }}</h3>
37
+ {% if profile.url %}<a class="academic-profile__link" href="{{ profile.url }}" target="_blank" rel="noopener">{% t 'academic_dashboard.profiles.view_profile' %}</a>{% endif %}
38
+ </header>
39
+ <dl class="academic-profile__meta">
40
+ {% if key == 'google_scholar' %}
41
+ {% if profile.user_id %}<div><dt>{% t 'academic_dashboard.profiles.user_id' %}</dt><dd>{{ profile.user_id }}</dd></div>{% endif %}
42
+ {% if academic.citations.metrics.total %}<div><dt>{% t 'academic_dashboard.profiles.total_citations' %}</dt><dd data-citation-metric="total">{{ academic.citations.metrics.total }}</dd></div>{% endif %}
43
+ {% if academic.citations.metrics.h_index %}<div><dt>{% t 'academic_dashboard.profiles.h_index' %}</dt><dd data-citation-metric="h_index">{{ academic.citations.metrics.h_index }}</dd></div>{% endif %}
44
+ {% if academic.citations.metrics.i10_index %}<div><dt>{% t 'academic_dashboard.profiles.i10_index' %}</dt><dd data-citation-metric="i10_index">{{ academic.citations.metrics.i10_index }}</dd></div>{% endif %}
45
+ {% endif %}
46
+ {% if key == 'orcid' and profile.sync %}
47
+ <div><dt>{% t 'academic_dashboard.profiles.sync_scope' %}</dt><dd>{{ profile.sync.scope | join: ', ' }}</dd></div>
48
+ <div><dt>{% t 'academic_dashboard.profiles.last_synced' %}</dt><dd data-profile-last-synced>{{ profile.sync.last_synced | localize_date: date_format }}</dd></div>
49
+ {% endif %}
50
+ {% if key != 'google_scholar' and key != 'orcid' %}
51
+ {% if profile.projects %}<div><dt>{% t 'academic_dashboard.profiles.featured_projects' %}</dt><dd>{{ profile.projects | array_to_sentence_string }}</dd></div>{% endif %}
52
+ {% if profile.collections %}<div><dt>{% t 'academic_dashboard.profiles.collections' %}</dt><dd>{{ profile.collections | array_to_sentence_string }}</dd></div>{% endif %}
53
+ {% endif %}
54
+ {% if profile.auto_update %}<div><dt>{% t 'academic_dashboard.profiles.auto_update' %}</dt><dd>{{ profile.auto_update | default: false }}</dd></div>{% endif %}
55
+ </dl>
56
+ </article>
57
+ {% endif %}
58
+ {% endfor %}
59
+ </div>
60
+ </section>
61
+
62
+ {% if citation_metrics %}
63
+ <section class="academic-dashboard__citations" aria-labelledby="citation-metrics-heading">
64
+ <h2 id="citation-metrics-heading">{% t 'academic_dashboard.citations.title' %}</h2>
65
+ <div class="citation-metrics">
66
+ <div class="citation-metrics__totals">
67
+ <div class="metric" data-citation-metric-card>
68
+ <dt>{% t 'academic_dashboard.citations.total' %}</dt>
69
+ <dd data-citation-metric="total">{{ citation_metrics.total | default: 0 }}</dd>
70
+ </div>
71
+ <div class="metric">
72
+ <dt>{% t 'academic_dashboard.citations.h_index' %}</dt>
73
+ <dd data-citation-metric="h_index">{{ citation_metrics.h_index | default: 0 }}</dd>
74
+ </div>
75
+ <div class="metric">
76
+ <dt>{% t 'academic_dashboard.citations.i10_index' %}</dt>
77
+ <dd data-citation-metric="i10_index">{{ citation_metrics.i10_index | default: 0 }}</dd>
78
+ </div>
79
+ </div>
80
+ <aside class="citation-metrics__details">
81
+ {% if since_stats %}
82
+ <h3>{% t 'academic_dashboard.citations.since_title' period: since_period %}</h3>
83
+ <ul>
84
+ <li><strong>{% t 'academic_dashboard.citations.total_label' %}</strong> <span data-citation-metric="since_total">{{ since_stats.total }}</span></li>
85
+ <li><strong>{% t 'academic_dashboard.citations.h_index_label' %}</strong> <span data-citation-metric="since_h_index">{{ since_stats.h_index }}</span></li>
86
+ <li><strong>{% t 'academic_dashboard.citations.i10_index_label' %}</strong> <span data-citation-metric="since_i10">{{ since_stats.i10_index }}</span></li>
87
+ </ul>
88
+ {% endif %}
89
+ {% if academic.citations.per_publication %}
90
+ <details>
91
+ <summary>{% t 'academic_dashboard.citations.per_publication' %}</summary>
92
+ <ul class="citation-metrics__per-publication">
93
+ {% for keyval in academic.citations.per_publication %}
94
+ <li>
95
+ <span class="citation-metrics__pub-id">{{ keyval[0] }}</span>
96
+ <span class="citation-metrics__pub-value" data-publication-citations="{{ keyval[0] }}">{{ keyval[1].total }}</span>
97
+ {% if keyval[1].last_updated %}
98
+ {% assign publication_updated = keyval[1].last_updated | localize_date: date_format %}
99
+ <span class="citation-metrics__pub-updated">{% t 'academic_dashboard.citations.updated' date: publication_updated %}</span>
100
+ {% endif %}
101
+ </li>
102
+ {% endfor %}
103
+ </ul>
104
+ </details>
105
+ {% endif %}
106
+ </aside>
107
+ {% if academic.citations.yearly_totals %}
108
+ <div class="citation-metrics__timeline" data-citation-timeline>
109
+ <h3>{% t 'academic_dashboard.citations.timeline' %}</h3>
110
+ <ul>
111
+ {% for year in academic.citations.yearly_totals %}
112
+ <li data-year="{{ year[0] }}"><span>{{ year[0] }}</span><span data-year-total>{{ year[1] }}</span></li>
113
+ {% endfor %}
114
+ </ul>
115
+ </div>
116
+ {% endif %}
117
+ </div>
118
+ </section>
119
+ {% endif %}
120
+
121
+ {% if publication_data.bibliography %}
122
+ <section class="academic-dashboard__bibliography" aria-labelledby="bibliography-heading">
123
+ <h2 id="bibliography-heading">{% t 'academic_dashboard.bibliography.title' %}</h2>
124
+ <p>{% t 'academic_dashboard.bibliography.description' %}</p>
125
+ <div class="bibliography-export">
126
+ <textarea readonly id="bibliography-export" data-bibliography>{{ publication_data.bibliography | join: '\n\n' }}</textarea>
127
+ <div class="bibliography-export__actions">
128
+ <button type="button" class="btn" data-copy-bibliography data-target="bibliography-export">{% t 'academic_dashboard.bibliography.copy' %}</button>
129
+ {% if academic.bibliography.exports %}
130
+ {% for export in academic.bibliography.exports %}
131
+ <a class="btn btn--ghost" href="{{ '/assets/publications/' | append: export.filename | relative_url }}" download>{% t 'academic_dashboard.bibliography.download' format: export.format | upcase %}</a>
132
+ {% endfor %}
133
+ {% endif %}
134
+ </div>
135
+ </div>
136
+ </section>
137
+ {% endif %}
138
+
139
+ {% if academic.submissions %}
140
+ <section class="academic-dashboard__submissions" id="submission-tracker" aria-labelledby="submission-tracker-heading">
141
+ <h2 id="submission-tracker-heading">{% t 'academic_dashboard.submissions.title' %}</h2>
142
+ <p>{% t 'academic_dashboard.submissions.description' %}</p>
143
+ <div class="submission-controls">
144
+ <label for="submission-filter">{% t 'academic_dashboard.submissions.filter_label' %}</label>
145
+ <select id="submission-filter" data-submission-filter>
146
+ <option value="">{% t 'academic_dashboard.submissions.all_statuses' %}</option>
147
+ {% assign statuses = academic.submissions | map: 'status' | uniq %}
148
+ {% for status in statuses %}
149
+ <option value="{{ status | slugify }}">{{ status }}</option>
150
+ {% endfor %}
151
+ </select>
152
+ </div>
153
+ <ul class="submission-list">
154
+ {% for submission in academic.submissions %}
155
+ <li class="submission-card" data-submission-status="{{ submission.status | slugify }}">
156
+ <article>
157
+ <header>
158
+ <h3>{{ submission.title }}</h3>
159
+ <p class="submission-venue">{{ submission.venue }} · <span class="submission-type">{{ submission.type | capitalize }}</span></p>
160
+ </header>
161
+ <dl class="submission-meta">
162
+ {% if submission.deadline %}<div><dt>{% t 'academic_dashboard.submissions.deadline' %}</dt><dd>{{ submission.deadline | localize_date: date_format }}</dd></div>{% endif %}
163
+ {% if submission.status %}<div><dt>{% t 'academic_dashboard.submissions.status' %}</dt><dd><span class="status-badge">{{ submission.status }}</span></dd></div>{% endif %}
164
+ {% if submission.submitted %}<div><dt>{% t 'academic_dashboard.submissions.submitted' %}</dt><dd>{{ submission.submitted | localize_date: date_format }}</dd></div>{% endif %}
165
+ {% if submission.collaborators %}<div><dt>{% t 'academic_dashboard.submissions.collaborators' %}</dt><dd>{{ submission.collaborators | array_to_sentence_string }}</dd></div>{% endif %}
166
+ </dl>
167
+ {% if submission.notes %}<p class="submission-notes">{{ submission.notes }}</p>{% endif %}
168
+ </article>
169
+ </li>
170
+ {% endfor %}
171
+ </ul>
172
+ </section>
173
+ {% endif %}
174
+
175
+ {% if academic.workflow %}
176
+ <section class="academic-dashboard__workflow" aria-labelledby="workflow-heading">
177
+ <h2 id="workflow-heading">{% t 'academic_dashboard.workflow.title' %}</h2>
178
+ <div class="workflow-grid">
179
+ {% if academic.workflow.peer_review %}
180
+ <article class="workflow-panel" aria-label="{% t 'academic_dashboard.workflow.peer_review.aria_label' %}">
181
+ <h3>{% t 'academic_dashboard.workflow.peer_review.title' %}</h3>
182
+ <ul class="workflow-list">
183
+ {% for review in academic.workflow.peer_review %}
184
+ <li>
185
+ <strong>{{ review.title }}</strong>
186
+ <p>{{ review.journal }}</p>
187
+ <p>{% t 'academic_dashboard.workflow.peer_review.status_label' %} <span class="status-badge">{{ review.status }}</span></p>
188
+ {% assign assigned_label = review.assigned_date | localize_date: date_format %}
189
+ {% assign due_label = review.due_date | localize_date: date_format %}
190
+ <p>{% t 'academic_dashboard.workflow.peer_review.dates' assigned: assigned_label, due: due_label %}</p>
191
+ {% if review.notes %}<p class="workflow-notes">{{ review.notes }}</p>{% endif %}
192
+ </li>
193
+ {% endfor %}
194
+ </ul>
195
+ </article>
196
+ {% endif %}
197
+ {% if academic.workflow.collaborations %}
198
+ <article class="workflow-panel" aria-label="{% t 'academic_dashboard.workflow.collaborations.aria_label' %}">
199
+ <h3>{% t 'academic_dashboard.workflow.collaborations.title' %}</h3>
200
+ <ul class="workflow-list">
201
+ {% for collab in academic.workflow.collaborations %}
202
+ <li>
203
+ <strong>{{ collab.name }}</strong>
204
+ <p>{{ collab.role }} · {{ collab.stage }}</p>
205
+ {% if collab.contact %}<p>{% t 'academic_dashboard.workflow.collaborations.contact' %} <a href="mailto:{{ collab.contact }}">{{ collab.contact }}</a></p>{% endif %}
206
+ {% if collab.notes %}<p class="workflow-notes">{{ collab.notes }}</p>{% endif %}
207
+ </li>
208
+ {% endfor %}
209
+ </ul>
210
+ </article>
211
+ {% endif %}
212
+ </div>
213
+ </section>
214
+ {% endif %}
215
+
216
+ {% if academic.calendar and academic.calendar.events %}
217
+ <section class="academic-dashboard__calendar" aria-labelledby="academic-calendar-heading">
218
+ <h2 id="academic-calendar-heading">{% t 'academic_dashboard.calendar.title' %}</h2>
219
+ {% if academic.calendar.description %}<p>{{ academic.calendar.description }}</p>{% endif %}
220
+ <div class="calendar-controls">
221
+ <label for="calendar-filter">{% t 'academic_dashboard.calendar.filter_label' %}</label>
222
+ <select id="calendar-filter" data-calendar-filter>
223
+ <option value="">{% t 'academic_dashboard.calendar.all_types' %}</option>
224
+ {% assign event_types = academic.calendar.events | map: 'type' | uniq %}
225
+ {% for type in event_types %}
226
+ <option value="{{ type | slugify }}">{{ type | capitalize }}</option>
227
+ {% endfor %}
228
+ </select>
229
+ </div>
230
+ <ul class="calendar-list" data-academic-calendar>
231
+ {% for event in academic.calendar.events %}
232
+ <li data-event-type="{{ event.type | slugify }}">
233
+ <article>
234
+ <header>
235
+ <h3>{{ event.name }}</h3>
236
+ <p class="calendar-event__type">{{ event.type | capitalize }} · {{ event.location }}</p>
237
+ </header>
238
+ <dl class="calendar-event__meta">
239
+ <div><dt>{% t 'academic_dashboard.calendar.starts' %}</dt><dd>{{ event.start_date | localize_date: datetime_format }}</dd></div>
240
+ <div><dt>{% t 'academic_dashboard.calendar.ends' %}</dt><dd>{{ event.end_date | localize_date: datetime_format }}</dd></div>
241
+ {% if event.submission_deadline %}<div><dt>{% t 'academic_dashboard.calendar.submission_deadline' %}</dt><dd>{{ event.submission_deadline | localize_date: datetime_format }}</dd></div>{% endif %}
242
+ </dl>
243
+ </article>
244
+ </li>
245
+ {% endfor %}
246
+ </ul>
247
+ </section>
248
+ {% endif %}
249
+
250
+ {% if academic.funding %}
251
+ <section class="academic-dashboard__funding" aria-labelledby="funding-heading">
252
+ <h2 id="funding-heading">{% t 'academic_dashboard.funding.title' %}</h2>
253
+ <ul class="funding-list">
254
+ {% for grant in academic.funding %}
255
+ <li>
256
+ <article>
257
+ <header>
258
+ <h3>{{ grant.title }}</h3>
259
+ <p>{{ grant.agency }} · {{ grant.role }}</p>
260
+ </header>
261
+ <dl class="funding-meta">
262
+ {% if grant.amount %}<div><dt>{% t 'academic_dashboard.funding.amount' %}</dt><dd>{{ grant.amount }}</dd></div>{% endif %}
263
+ {% if grant.status %}<div><dt>{% t 'academic_dashboard.funding.status' %}</dt><dd>{{ grant.status }}</dd></div>{% endif %}
264
+ {% if grant.deadline or grant.period %}<div><dt>{% t 'academic_dashboard.funding.decision_deadline' %}</dt><dd>{% if grant.deadline %}{{ grant.deadline | localize_date: date_format }}{% else %}{{ grant.period }}{% endif %}</dd></div>{% endif %}
265
+ </dl>
266
+ {% if grant.focus %}<p class="funding-focus">{{ grant.focus }}</p>{% endif %}
267
+ </article>
268
+ </li>
269
+ {% endfor %}
270
+ </ul>
271
+ </section>
272
+ {% endif %}
273
+
274
+ {% if academic.collaboration_opportunities %}
275
+ <section class="academic-dashboard__opportunities" aria-labelledby="collaboration-heading">
276
+ <h2 id="collaboration-heading">{% t 'academic_dashboard.opportunities.title' %}</h2>
277
+ <ul class="opportunity-list">
278
+ {% for opportunity in academic.collaboration_opportunities %}
279
+ <li>
280
+ <article>
281
+ <header>
282
+ <h3>{{ opportunity.title }}</h3>
283
+ {% if opportunity.tags %}<p class="opportunity-tags">{{ opportunity.tags | array_to_sentence_string }}</p>{% endif %}
284
+ </header>
285
+ <p>{{ opportunity.description }}</p>
286
+ {% if opportunity.contact %}<p class="opportunity-contact"><a href="mailto:{{ opportunity.contact }}">{{ opportunity.contact }}</a></p>{% endif %}
287
+ </article>
288
+ </li>
289
+ {% endfor %}
290
+ </ul>
291
+ </section>
292
+ {% endif %}
293
+
294
+ {% if academic.mentorship %}
295
+ <section class="academic-dashboard__mentorship" aria-labelledby="mentorship-heading">
296
+ <h2 id="mentorship-heading">{% t 'academic_dashboard.mentorship.title' %}</h2>
297
+ <ul class="mentorship-list">
298
+ {% for program in academic.mentorship %}
299
+ <li>
300
+ <article>
301
+ <header>
302
+ <h3>{{ program.program }}</h3>
303
+ </header>
304
+ <dl class="mentorship-meta">
305
+ {% if program.offering %}<div><dt>{% t 'academic_dashboard.mentorship.offering' %}</dt><dd>{{ program.offering }}</dd></div>{% endif %}
306
+ {% if program.cohorts %}<div><dt>{% t 'academic_dashboard.mentorship.cohorts' %}</dt><dd>{{ program.cohorts }}</dd></div>{% endif %}
307
+ {% if program.schedule %}<div><dt>{% t 'academic_dashboard.mentorship.schedule' %}</dt><dd>{{ program.schedule }}</dd></div>{% endif %}
308
+ {% if program.application_deadline %}<div><dt>{% t 'academic_dashboard.mentorship.application_deadline' %}</dt><dd>{{ program.application_deadline | localize_date: date_format }}</dd></div>{% endif %}
309
+ </dl>
310
+ {% if program.contact %}<p><a href="mailto:{{ program.contact }}">{{ program.contact }}</a></p>{% endif %}
311
+ </article>
312
+ </li>
313
+ {% endfor %}
314
+ </ul>
315
+ </section>
316
+ {% endif %}
317
+
318
+ {% include components/open-science-badges.html badges=academic.badges %}
319
+
320
+ {% if academic.networking %}
321
+ <section class="academic-dashboard__networking" aria-labelledby="networking-heading">
322
+ <h2 id="networking-heading">{% t 'academic_dashboard.networking.title' %}</h2>
323
+ <div class="networking-grid">
324
+ {% if academic.networking.platforms %}
325
+ <article>
326
+ <h3>{% t 'academic_dashboard.networking.communities' %}</h3>
327
+ <ul>
328
+ {% for platform in academic.networking.platforms %}
329
+ <li><a href="{{ platform.url }}" target="_blank" rel="noopener">{{ platform.name }}</a> · {{ platform.role }}</li>
330
+ {% endfor %}
331
+ </ul>
332
+ </article>
333
+ {% endif %}
334
+ {% if academic.networking.mentorship_channels %}
335
+ <article>
336
+ <h3>{% t 'academic_dashboard.networking.mentorship_channels' %}</h3>
337
+ <ul>
338
+ {% for channel in academic.networking.mentorship_channels %}
339
+ <li><strong>{{ channel.channel }}</strong>: {{ channel.description }}</li>
340
+ {% endfor %}
341
+ </ul>
342
+ </article>
343
+ {% endif %}
344
+ </div>
345
+ </section>
346
+ {% endif %}
347
+ </section>