mirror of
https://github.com/ClaperCo/Claper.git
synced 2026-02-24 12:09:59 +01:00
## ⚠️ Breaking changes - S3 variables are now named: S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_REGION and S3_BUCKET - Users now have roles. Refer to the `roles` table and assign a role to a user with the `role_id` column in the `users` table. ## Features - Add Admin Panel to manage users and presentations - Add user roles: user, admin - Add `LANGUAGES` setting to configure available languages in the app - Add hideable presenter attendee count (#183 #155) - Add Hungarian translation (#161) - Add Latvian translation (#163) - Add custom S3 endpoint with `S3_SCHEME`, `S3_HOST`, `S3_PORT` and `S3_PUBLIC_URL` ## Fixes and improvements - Upgrade JS dependencies - Upgrade Elixir dependencies, including Phoenix Live View to 1.0.17 - Upgrade to Tailwind 4+ - Refactor view templates to use {} instead of <%= %> - Fix event name validation to be required - Docker image is now using Ubuntu instead of Alpine for better dependencies support - Fix scrollbar not showing in event manager when no presentation file (#164) (@aryel780) - Fix settings scroll for small screen (#168) - Fix duplicate key quiz when duplicate (#182) - Fix email change confirmation (#172) - Fix italian translation (#179) - Fix random poll choices (#184)
198 lines
5.3 KiB
JavaScript
198 lines
5.3 KiB
JavaScript
// LiveView hooks for client-side functionality
|
|
|
|
const Hooks = {
|
|
// Hook for handling CSV downloads from LiveView
|
|
CSVDownloader: {
|
|
mounted() {
|
|
this.handleEvent("download_csv", ({ filename, content }) => {
|
|
// Create a Blob with the CSV content
|
|
const blob = new Blob([content], { type: "text/csv" });
|
|
|
|
// Create a temporary URL for the Blob
|
|
const url = window.URL.createObjectURL(blob);
|
|
|
|
// Create a temporary link element
|
|
const link = document.createElement("a");
|
|
link.href = url;
|
|
link.setAttribute("download", filename);
|
|
|
|
// Append the link to the document body
|
|
document.body.appendChild(link);
|
|
|
|
// Trigger the download
|
|
link.click();
|
|
|
|
// Clean up
|
|
window.URL.revokeObjectURL(url);
|
|
document.body.removeChild(link);
|
|
});
|
|
}
|
|
},
|
|
|
|
// Hook for User Growth Chart
|
|
UserGrowthChart: {
|
|
mounted() {
|
|
// Import Chart.js dynamically
|
|
import("chart.js/auto").then(({ default: Chart }) => {
|
|
const ctx = this.el.getContext("2d");
|
|
const labels = JSON.parse(this.el.dataset.labels);
|
|
const values = JSON.parse(this.el.dataset.values);
|
|
|
|
this.chart = new Chart(ctx, {
|
|
type: "line",
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: "New Users",
|
|
data: values,
|
|
borderColor: "#111827",
|
|
backgroundColor: "rgba(17, 24, 39, 0.05)",
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0,
|
|
pointHoverRadius: 0,
|
|
pointBackgroundColor: "transparent",
|
|
pointBorderColor: "transparent"
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
interaction: {
|
|
intersect: false,
|
|
mode: 'index'
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
display: false
|
|
},
|
|
tooltip: {
|
|
enabled: true,
|
|
backgroundColor: "rgba(17, 24, 39, 0.9)",
|
|
titleColor: "#fff",
|
|
bodyColor: "#fff",
|
|
borderColor: "#111827",
|
|
borderWidth: 1,
|
|
cornerRadius: 4,
|
|
displayColors: false,
|
|
padding: 8,
|
|
titleFont: {
|
|
size: 12
|
|
},
|
|
bodyFont: {
|
|
size: 14,
|
|
weight: 'bold'
|
|
},
|
|
callbacks: {
|
|
label: function(context) {
|
|
return context.parsed.y + ' users';
|
|
}
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
display: false
|
|
},
|
|
y: {
|
|
display: false
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
destroyed() {
|
|
if (this.chart) {
|
|
this.chart.destroy();
|
|
}
|
|
}
|
|
},
|
|
|
|
// Hook for Event Creation Chart
|
|
EventCreationChart: {
|
|
mounted() {
|
|
// Import Chart.js dynamically
|
|
import("chart.js/auto").then(({ default: Chart }) => {
|
|
const ctx = this.el.getContext("2d");
|
|
const labels = JSON.parse(this.el.dataset.labels);
|
|
const values = JSON.parse(this.el.dataset.values);
|
|
|
|
this.chart = new Chart(ctx, {
|
|
type: "line",
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: "New Events",
|
|
data: values,
|
|
borderColor: "#111827",
|
|
backgroundColor: "rgba(17, 24, 39, 0.05)",
|
|
borderWidth: 2,
|
|
tension: 0.4,
|
|
fill: true,
|
|
pointRadius: 0,
|
|
pointHoverRadius: 0,
|
|
pointBackgroundColor: "transparent",
|
|
pointBorderColor: "transparent"
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
interaction: {
|
|
intersect: false,
|
|
mode: 'index'
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
display: false
|
|
},
|
|
tooltip: {
|
|
enabled: true,
|
|
backgroundColor: "rgba(17, 24, 39, 0.9)",
|
|
titleColor: "#fff",
|
|
bodyColor: "#fff",
|
|
borderColor: "#111827",
|
|
borderWidth: 1,
|
|
cornerRadius: 4,
|
|
displayColors: false,
|
|
padding: 8,
|
|
titleFont: {
|
|
size: 12
|
|
},
|
|
bodyFont: {
|
|
size: 14,
|
|
weight: 'bold'
|
|
},
|
|
callbacks: {
|
|
label: function(context) {
|
|
return context.parsed.y + ' events';
|
|
}
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
display: false
|
|
},
|
|
y: {
|
|
display: false
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
destroyed() {
|
|
if (this.chart) {
|
|
this.chart.destroy();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
export default Hooks;
|