Improve interaction and attendees submissions panes

This commit is contained in:
Alex Lion
2026-02-13 18:42:09 +01:00
parent d0e88e136b
commit 2a1d990ad0
5 changed files with 610 additions and 583 deletions

View File

@@ -814,6 +814,8 @@ defmodule ClaperWeb.EventLive.Manage do
defp apply_action(socket, :add_poll, _params) do
socket
|> assign(:create, "poll")
|> assign(:interaction_modal, true)
|> assign(:create_action, :new)
|> assign(:poll, %Polls.Poll{
poll_opts: [%Polls.PollOpt{content: gettext("Yes")}, %Polls.PollOpt{content: gettext("No")}]
})
@@ -832,6 +834,8 @@ defmodule ClaperWeb.EventLive.Manage do
defp apply_action(socket, :add_form, _params) do
socket
|> assign(:create, "form")
|> assign(:interaction_modal, true)
|> assign(:create_action, :new)
|> assign(:form, %Forms.Form{
fields: [
%Forms.Field{name: gettext("Name"), type: "text"},
@@ -843,6 +847,8 @@ defmodule ClaperWeb.EventLive.Manage do
defp apply_action(socket, :add_embed, _params) do
socket
|> assign(:create, "embed")
|> assign(:interaction_modal, true)
|> assign(:create_action, :new)
|> assign(:embed, %Embeds.Embed{})
end
@@ -875,6 +881,8 @@ defmodule ClaperWeb.EventLive.Manage do
defp apply_action(socket, :add_quiz, _params) do
socket
|> assign(:create, "quiz")
|> assign(:interaction_modal, true)
|> assign(:create_action, :new)
|> assign(:quiz, %Quizzes.Quiz{
presentation_file_id: socket.assigns.event.presentation_file.id,
quiz_questions: [

View File

@@ -549,7 +549,7 @@
<!-- Slide Preview (only if presentation has slides) -->
<div
:if={@event.presentation_file.length > 0}
class="flex-shrink-0 h-1/3"
class="flex-shrink-0 h-2/5"
>
<.live_component
id="slide-preview"
@@ -564,7 +564,7 @@
<div class="flex-1 flex flex-col lg:flex-row overflow-hidden">
<!-- Interactions Panel -->
<div
class="lg:w-1/3 flex-shrink-0 p-4 overflow-y-auto border-b lg:border-b-0 lg:border-r border-gray-200"
class="lg:w-1/3 flex-shrink-0 p-4 overflow-y-auto"
data-tg-order="2"
data-tg-title={gettext("Your interactions")}
data-tg-tour={"<p class='mb-3'>#{gettext("This section contains all your interactions for the current slide.")}</p>"}
@@ -578,314 +578,23 @@
/>
</div>
<!-- Options Panel (Messages, Questions, etc.) -->
<!-- Audience Responses Panel -->
<div
class="flex-1 flex flex-col overflow-hidden"
class="flex-1 flex flex-col overflow-hidden p-4"
data-tg-title={gettext("Attendees interactions")}
data-tg-order="3"
data-tg-tour={"<p class='mb-3'>#{gettext("Here you'll find all interactions from your attendees.")}</p>"}
data-tg-group="manage"
>
<div class="bg-white rounded-xl shadow-base m-4 flex-1 flex flex-col overflow-hidden">
<div class="px-4 py-3 border-b border-gray-200 flex items-center justify-between">
<div class="flex items-center gap-x-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5 text-gray-500"
>
<path
fill-rule="evenodd"
d="M10 2c-2.236 0-4.43.18-6.57.524C1.993 2.755 1 4.014 1 5.426v5.148c0 1.413.993 2.67 2.43 2.902.848.137 1.705.248 2.57.331v3.443a.75.75 0 0 0 1.28.53l3.58-3.579a.78.78 0 0 1 .527-.224 41.202 41.202 0 0 0 5.183-.5c1.437-.232 2.43-1.49 2.43-2.903V5.426c0-1.413-.993-2.67-2.43-2.902A41.289 41.289 0 0 0 10 2Zm0 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM8 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm5 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
clip-rule="evenodd"
/>
</svg>
<span class="font-semibold text-gray-700">{gettext("Options")}</span>
</div>
<button class="text-sm text-gray-500 hover:text-gray-700 flex items-center gap-x-1">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path d="M2.09 15a1 1 0 0 0 1-1V8a1 1 0 1 0-2 0v6a1 1 0 0 0 1 1ZM5.765 13H4.09V8c.663 0 1.218-.466 1.556-1.037a4.02 4.02 0 0 1 1.358-1.377c.478-.292.907-.706.989-1.26V4.32a9.03 9.03 0 0 0 0-2.642c-.028-.194.048-.394.224-.479A2 2 0 0 1 11.09 3c0 .812-.08 1.605-.235 2.371a.521.521 0 0 0 .502.629h1.733c1.104 0 2.01.898 1.901 1.997a19.831 19.831 0 0 1-1.081 4.788c-.27.747-.998 1.215-1.793 1.215H9.414c-.215 0-.428-.035-.632-.103l-2.384-.794A2.002 2.002 0 0 0 5.765 13Z" />
</svg>
{gettext("Sort by popularity")}
</button>
</div>
<!-- Tabs -->
<ul
id="menu"
phx-update="replace"
class="flex items-center gap-x-1 px-4 py-2 border-b border-gray-200 overflow-x-auto"
>
<li>
<button
phx-click="list-tab"
phx-value-tab="posts"
class={"px-3 py-1.5 rounded-full text-sm font-medium transition-colors #{if @list_tab == :posts, do: "bg-secondary-500 text-white", else: "text-gray-600 hover:bg-gray-100"}"}
>
{gettext("Chat")} ({@post_count})
</button>
</li>
<li>
<button
phx-click="list-tab"
phx-value-tab="questions"
class={"px-3 py-1.5 rounded-full text-sm font-medium transition-colors #{if @list_tab == :questions, do: "bg-secondary-500 text-white", else: "text-gray-600 hover:bg-gray-100"}"}
>
{gettext("Question")} ({@question_count})
</button>
</li>
<li>
<button
phx-click="list-tab"
phx-value-tab="pinned_posts"
class={"px-3 py-1.5 rounded-full text-sm font-medium transition-colors #{if @list_tab == :pinned_posts, do: "bg-secondary-500 text-white", else: "text-gray-600 hover:bg-gray-100"}"}
>
{gettext("Pinned")} ({@pinned_post_count})
</button>
</li>
<li>
<button
phx-click="list-tab"
phx-value-tab="forms"
class={"px-3 py-1.5 rounded-full text-sm font-medium transition-colors #{if @list_tab == :forms, do: "bg-secondary-500 text-white", else: "text-gray-600 hover:bg-gray-100"}"}
>
{gettext("Forms")} ({@form_submit_count})
</button>
</li>
</ul>
<!-- Tab Content -->
<div class="flex-1 overflow-y-auto">
<%= if @list_tab == :posts do %>
<div
:if={@post_count == 0}
class="h-full flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-16 w-16 mb-3"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"
/>
</svg>
<p class="text-sm">{gettext("Messages from attendees will appear here.")}</p>
</div>
<div
:if={@post_count > 0}
id="post-list"
class="p-4 space-y-3"
phx-update="stream"
phx-hook="ScrollIntoDiv"
>
<.live_component
:for={{id, post} <- @streams.posts}
module={ClaperWeb.EventLive.ManageablePostComponent}
id={id}
event={@event}
post={post}
/>
</div>
<% end %>
<%= if @list_tab == :questions do %>
<div
:if={@question_count == 0}
class="h-full flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-16 h-16 mb-3"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z"
/>
</svg>
<p class="text-sm">{gettext("Questions will appear here.")}</p>
</div>
<div :if={@question_count > 0} class="flex flex-col h-full">
<div class="px-4 py-2 flex items-center gap-x-2">
<button
class="px-3 py-1 text-xs rounded-md bg-gray-100 text-gray-700 hover:bg-gray-200 flex items-center gap-x-1"
phx-click="sort-questions"
phx-value-sort="likes"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-3 h-3"
>
<path d="M2.09 15a1 1 0 0 0 1-1V8a1 1 0 1 0-2 0v6a1 1 0 0 0 1 1ZM5.765 13H4.09V8c.663 0 1.218-.466 1.556-1.037a4.02 4.02 0 0 1 1.358-1.377c.478-.292.907-.706.989-1.26V4.32a9.03 9.03 0 0 0 0-2.642c-.028-.194.048-.394.224-.479A2 2 0 0 1 11.09 3c0 .812-.08 1.605-.235 2.371a.521.521 0 0 0 .502.629h1.733c1.104 0 2.01.898 1.901 1.997a19.831 19.831 0 0 1-1.081 4.788c-.27.747-.998 1.215-1.793 1.215H9.414c-.215 0-.428-.035-.632-.103l-2.384-.794A2.002 2.002 0 0 0 5.765 13Z" />
</svg>
{gettext("Popularity")}
</button>
<button
class="px-3 py-1 text-xs rounded-md bg-gray-100 text-gray-700 hover:bg-gray-200 flex items-center gap-x-1"
phx-click="sort-questions"
phx-value-sort="date"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-3 h-3"
>
<path
fill-rule="evenodd"
d="M1 8a7 7 0 1 1 14 0A7 7 0 0 1 1 8Zm7.75-4.25a.75.75 0 0 0-1.5 0V8c0 .414.336.75.75.75h3.25a.75.75 0 0 0 0-1.5h-2.5v-3.5Z"
clip-rule="evenodd"
/>
</svg>
{gettext("Date")}
</button>
</div>
<div
id="question-list"
class="flex-1 overflow-y-auto p-4 space-y-3"
phx-update="stream"
data-use-parent="true"
phx-hook="ScrollIntoDiv"
>
<.live_component
:for={{id, post} <- @streams.questions}
module={ClaperWeb.EventLive.ManageablePostComponent}
id={id}
event={@event}
post={post}
/>
</div>
</div>
<% end %>
<%= if @list_tab == :pinned_posts do %>
<div
:if={@pinned_post_count == 0}
class="h-full flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-16 w-16 mb-3"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M15 4.5l-4 4l-4 1.5l-1.5 1.5l7 7l1.5 -1.5l1.5 -4l4 -4" /><path d="M9 15l-4.5 4.5" /><path d="M14.5 4l5.5 5.5" />
</svg>
<p class="text-sm">{gettext("Pinned messages will appear here.")}</p>
</div>
<div
:if={@pinned_post_count > 0}
id="pinned-post-list"
class="p-4 space-y-3"
phx-update="stream"
phx-hook="ScrollIntoDiv"
>
<.live_component
:for={{id, post} <- @streams.pinned_posts}
module={ClaperWeb.EventLive.ManageablePostComponent}
id={id}
event={@event}
post={post}
/>
</div>
<% end %>
<%= if @list_tab == :forms do %>
<div
:if={@form_submit_count == 0}
class="h-full flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-16 w-16 mb-3"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3a3 3 0 0 0 -3 3v12a3 3 0 0 0 3 3"></path>
<path d="M6 3a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3"></path>
<path d="M13 7h7a1 1 0 0 1 1 1v8a1 1 0 0 1 -1 1h-7"></path>
<path d="M5 7h-1a1 1 0 0 0 -1 1v8a1 1 0 0 0 1 1h1"></path>
<path d="M17 12h.01"></path>
<path d="M13 12h.01"></path>
</svg>
<p class="text-sm">{gettext("Form submissions will appear here.")}</p>
</div>
<div
id="form-list"
class="p-4 space-y-3"
phx-update="stream"
data-forms-nb={@form_submit_count}
phx-hook="ScrollIntoDiv"
>
<div :for={{id, submission} <- @streams.form_submits} id={id}>
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200">
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-medium text-gray-900">
{submission.form.title}
</span>
<button
phx-click="delete-form-submit"
phx-value-id={submission.id}
phx-value-event_id={@event.uuid}
data-confirm={gettext("This cannot be undone, confirm ?")}
class="text-xs text-red-500 hover:text-red-700"
>
{gettext("Delete")}
</button>
</div>
<div class="flex items-start gap-x-3">
<%= if submission.attendee_identifier do %>
<img
class="h-8 w-8 rounded-full"
src={"https://api.dicebear.com/7.x/personas/svg?seed=#{submission.attendee_identifier}"}
/>
<% else %>
<img
class="h-8 w-8 rounded-full"
src={"https://api.dicebear.com/7.x/personas/svg?seed=#{submission.user_id}"}
/>
<% end %>
<div class="flex-1 text-sm text-gray-600">
<%= for res <- submission.response do %>
<p>
<span class="font-medium">{elem(res, 0)}:</span>
{elem(res, 1)}
</p>
<% end %>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
<ClaperWeb.EventLive.ManageAudienceResponsesComponent.render
list_tab={@list_tab}
post_count={@post_count}
question_count={@question_count}
pinned_post_count={@pinned_post_count}
form_submit_count={@form_submit_count}
streams={@streams}
event={@event}
/>
</div>
</div>
</main>

View File

@@ -0,0 +1,301 @@
defmodule ClaperWeb.EventLive.ManageAudienceResponsesComponent do
@moduledoc false
use Phoenix.Component
use Gettext, backend: ClaperWeb.Gettext
def render(assigns) do
~H"""
<div class="flex flex-col gap-2 border border-gray-200 rounded-2xl p-2 flex-1 overflow-hidden bg-gray-100">
<div class="flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 20 20"
fill="none"
class="shrink-0"
>
<path
fill-rule="evenodd"
d="M10 2c-2.236 0-4.43.18-6.57.524C1.993 2.755 1 4.014 1 5.426v5.148c0 1.413.993 2.67 2.43 2.902.848.137 1.705.248 2.57.331v3.443a.75.75 0 0 0 1.28.53l3.58-3.579a.78.78 0 0 1 .527-.224 41.202 41.202 0 0 0 5.183-.5c1.437-.232 2.43-1.49 2.43-2.903V5.426c0-1.413-.993-2.67-2.43-2.902A41.289 41.289 0 0 0 10 2Zm0 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM8 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm5 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
clip-rule="evenodd"
fill="#140553"
/>
</svg>
<span class="font-bold text-sm text-[#140553]">{gettext("Audience Responses")}</span>
</div>
<!-- Tabs -->
<ul
id="menu"
phx-update="replace"
class="flex items-center gap-x-1 overflow-x-auto"
>
<li>
<button
phx-click="list-tab"
phx-value-tab="posts"
class={"px-3 py-1.5 rounded-full text-sm font-medium transition-colors #{if @list_tab == :posts, do: "bg-secondary-500 text-white", else: "text-gray-600 hover:bg-gray-100"}"}
>
{gettext("Chat")} ({@post_count})
</button>
</li>
<li>
<button
phx-click="list-tab"
phx-value-tab="questions"
class={"px-3 py-1.5 rounded-full text-sm font-medium transition-colors #{if @list_tab == :questions, do: "bg-secondary-500 text-white", else: "text-gray-600 hover:bg-gray-100"}"}
>
{gettext("Question")} ({@question_count})
</button>
</li>
<li>
<button
phx-click="list-tab"
phx-value-tab="pinned_posts"
class={"px-3 py-1.5 rounded-full text-sm font-medium transition-colors #{if @list_tab == :pinned_posts, do: "bg-secondary-500 text-white", else: "text-gray-600 hover:bg-gray-100"}"}
>
{gettext("Pinned")} ({@pinned_post_count})
</button>
</li>
<li>
<button
phx-click="list-tab"
phx-value-tab="forms"
class={"px-3 py-1.5 rounded-full text-sm font-medium transition-colors #{if @list_tab == :forms, do: "bg-secondary-500 text-white", else: "text-gray-600 hover:bg-gray-100"}"}
>
{gettext("Forms")} ({@form_submit_count})
</button>
</li>
</ul>
<!-- Tab Content -->
<div class="flex-1 overflow-y-auto">
<%= if @list_tab == :posts do %>
<div
:if={@post_count == 0}
class="h-full flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-12 w-12 mb-3"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"
/>
</svg>
<p class="text-sm">{gettext("Messages from attendees will appear here.")}</p>
</div>
<div
:if={@post_count > 0}
id="post-list"
class="p-2 space-y-3"
phx-update="stream"
phx-hook="ScrollIntoDiv"
>
<.live_component
:for={{id, post} <- @streams.posts}
module={ClaperWeb.EventLive.ManageablePostComponent}
id={id}
event={@event}
post={post}
/>
</div>
<% end %>
<%= if @list_tab == :questions do %>
<div
:if={@question_count == 0}
class="h-full flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-12 h-12 mb-3"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z"
/>
</svg>
<p class="text-sm">{gettext("Questions will appear here.")}</p>
</div>
<div :if={@question_count > 0} class="flex flex-col h-full">
<div class="px-2 py-2 flex items-center gap-x-2">
<button
class="px-3 py-1 text-xs rounded-md bg-gray-100 text-gray-700 hover:bg-gray-200 flex items-center gap-x-1"
phx-click="sort-questions"
phx-value-sort="likes"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-3 h-3"
>
<path d="M2.09 15a1 1 0 0 0 1-1V8a1 1 0 1 0-2 0v6a1 1 0 0 0 1 1ZM5.765 13H4.09V8c.663 0 1.218-.466 1.556-1.037a4.02 4.02 0 0 1 1.358-1.377c.478-.292.907-.706.989-1.26V4.32a9.03 9.03 0 0 0 0-2.642c-.028-.194.048-.394.224-.479A2 2 0 0 1 11.09 3c0 .812-.08 1.605-.235 2.371a.521.521 0 0 0 .502.629h1.733c1.104 0 2.01.898 1.901 1.997a19.831 19.831 0 0 1-1.081 4.788c-.27.747-.998 1.215-1.793 1.215H9.414c-.215 0-.428-.035-.632-.103l-2.384-.794A2.002 2.002 0 0 0 5.765 13Z" />
</svg>
{gettext("Popularity")}
</button>
<button
class="px-3 py-1 text-xs rounded-md bg-gray-100 text-gray-700 hover:bg-gray-200 flex items-center gap-x-1"
phx-click="sort-questions"
phx-value-sort="date"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-3 h-3"
>
<path
fill-rule="evenodd"
d="M1 8a7 7 0 1 1 14 0A7 7 0 0 1 1 8Zm7.75-4.25a.75.75 0 0 0-1.5 0V8c0 .414.336.75.75.75h3.25a.75.75 0 0 0 0-1.5h-2.5v-3.5Z"
clip-rule="evenodd"
/>
</svg>
{gettext("Date")}
</button>
</div>
<div
id="question-list"
class="flex-1 overflow-y-auto p-2 space-y-3"
phx-update="stream"
data-use-parent="true"
phx-hook="ScrollIntoDiv"
>
<.live_component
:for={{id, post} <- @streams.questions}
module={ClaperWeb.EventLive.ManageablePostComponent}
id={id}
event={@event}
post={post}
/>
</div>
</div>
<% end %>
<%= if @list_tab == :pinned_posts do %>
<div
:if={@pinned_post_count == 0}
class="h-full flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-12 w-12 mb-3"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M15 4.5l-4 4l-4 1.5l-1.5 1.5l7 7l1.5 -1.5l1.5 -4l4 -4" /><path d="M9 15l-4.5 4.5" /><path d="M14.5 4l5.5 5.5" />
</svg>
<p class="text-sm">{gettext("Pinned messages will appear here.")}</p>
</div>
<div
:if={@pinned_post_count > 0}
id="pinned-post-list"
class="p-2 space-y-3"
phx-update="stream"
phx-hook="ScrollIntoDiv"
>
<.live_component
:for={{id, post} <- @streams.pinned_posts}
module={ClaperWeb.EventLive.ManageablePostComponent}
id={id}
event={@event}
post={post}
/>
</div>
<% end %>
<%= if @list_tab == :forms do %>
<div
:if={@form_submit_count == 0}
class="h-full flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-12 w-12 mb-3"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3a3 3 0 0 0 -3 3v12a3 3 0 0 0 3 3"></path>
<path d="M6 3a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3"></path>
<path d="M13 7h7a1 1 0 0 1 1 1v8a1 1 0 0 1 -1 1h-7"></path>
<path d="M5 7h-1a1 1 0 0 0 -1 1v8a1 1 0 0 0 1 1h1"></path>
<path d="M17 12h.01"></path>
<path d="M13 12h.01"></path>
</svg>
<p class="text-sm">{gettext("Form submissions will appear here.")}</p>
</div>
<div
id="form-list"
class="p-2 space-y-3"
phx-update="stream"
data-forms-nb={@form_submit_count}
phx-hook="ScrollIntoDiv"
>
<div :for={{id, submission} <- @streams.form_submits} id={id}>
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200">
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-medium text-gray-900">
{submission.form.title}
</span>
<button
phx-click="delete-form-submit"
phx-value-id={submission.id}
phx-value-event_id={@event.uuid}
data-confirm={gettext("This cannot be undone, confirm ?")}
class="text-xs text-red-500 hover:text-red-700"
>
{gettext("Delete")}
</button>
</div>
<div class="flex items-start gap-x-3">
<%= if submission.attendee_identifier do %>
<img
class="h-8 w-8 rounded-full"
src={"https://api.dicebear.com/7.x/personas/svg?seed=#{submission.attendee_identifier}"}
/>
<% else %>
<img
class="h-8 w-8 rounded-full"
src={"https://api.dicebear.com/7.x/personas/svg?seed=#{submission.user_id}"}
/>
<% end %>
<div class="flex-1 text-sm text-gray-600">
<%= for res <- submission.response do %>
<p>
<span class="font-medium">{elem(res, 0)}:</span>
{elem(res, 1)}
</p>
<% end %>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
"""
end
end

View File

@@ -3,143 +3,72 @@ defmodule ClaperWeb.EventLive.ManageInteractionListComponent do
def render(assigns) do
~H"""
<div class="bg-white rounded-xl shadow-base h-full flex flex-col">
<div class="px-4 py-3 border-b border-gray-200 flex items-center gap-x-2">
<div class="flex flex-col gap-2 border border-gray-200 rounded-2xl p-2">
<div class="flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5 text-gray-500"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
class="shrink-0"
>
<path
d="M19.7774 4.2209C18.7569 3.19837 17.5444 2.38746 16.2097 1.83473C14.8749 1.282 13.4442 0.998334 11.9995 1.00001C5.92463 1.00001 1 5.92484 1 12C1 15.0376 2.23185 17.7883 4.2226 19.7791C5.24314 20.8016 6.45558 21.6125 7.79032 22.1653C9.12506 22.718 10.5558 23.0017 12.0005 23C18.0754 23 23 18.0752 23 12C23 8.96242 21.7682 6.21174 19.7774 4.2209ZM18.3641 18.3611C17.529 19.1976 16.537 19.8611 15.445 20.3134C14.353 20.7658 13.1824 20.9982 12.0005 20.9972C7.03 20.9972 3.00083 16.9679 3.00083 11.9973C2.9999 10.8152 3.23227 9.64463 3.68461 8.55258C4.13695 7.46054 4.80037 6.46851 5.63684 5.63337C6.47183 4.79698 7.46367 4.13359 8.55551 3.68124C9.64734 3.22888 10.8177 2.99645 11.9995 2.99726C16.9691 2.99726 20.9982 7.02658 20.9982 11.9963C20.9991 13.1782 20.7666 14.3486 20.3143 15.4405C19.862 16.5324 19.1986 17.5243 18.3622 18.3593L18.3641 18.3611Z"
fill="#140553"
/>
<path
d="M17.1755 6.82391C16.4964 6.14349 15.6896 5.6039 14.8014 5.23611C13.9133 4.86831 12.9612 4.67956 11.9999 4.68067C7.9576 4.68067 4.68066 7.95774 4.68066 12.0002C4.68066 14.0215 5.50035 15.8518 6.82503 17.1766C7.50412 17.857 8.3109 18.3966 9.19905 18.7644C10.0872 19.1322 11.0393 19.3209 12.0005 19.3198C16.0429 19.3198 19.3198 16.0428 19.3198 12.0002C19.3198 9.97899 18.5001 8.14864 17.1755 6.82391ZM16.235 16.233C15.6793 16.7897 15.0192 17.2311 14.2926 17.5321C13.566 17.8332 12.7871 17.9878 12.0005 17.9872C8.69312 17.9872 6.01205 15.306 6.01205 11.9984C6.01143 11.2119 6.16605 10.4329 6.46705 9.70628C6.76804 8.97961 7.20949 8.3195 7.76609 7.76378C8.32171 7.20723 8.9817 6.76581 9.70822 6.4648C10.4347 6.1638 11.2135 6.00913 11.9999 6.00967C15.3068 6.00967 17.9878 8.69085 17.9878 11.9978C17.9884 12.7842 17.8337 13.5631 17.5327 14.2896C17.2317 15.0162 16.7903 15.6762 16.2338 16.2318L16.235 16.233Z"
fill="#140553"
/>
<path
fill-rule="evenodd"
d="M10 2c-2.236 0-4.43.18-6.57.524C1.993 2.755 1 4.014 1 5.426v5.148c0 1.413.993 2.67 2.43 2.902 1.168.188 2.352.327 3.55.414.28.02.521.18.642.413l1.713 3.293a.75.75 0 0 0 1.33 0l1.713-3.293a.783.783 0 0 1 .642-.413 41.102 41.102 0 0 0 3.55-.414c1.437-.231 2.43-1.49 2.43-2.902V5.426c0-1.413-.993-2.67-2.43-2.902A41.289 41.289 0 0 0 10 2ZM6.75 6a.75.75 0 0 0 0 1.5h6.5a.75.75 0 0 0 0-1.5h-6.5Zm0 2.5a.75.75 0 0 0 0 1.5h3.5a.75.75 0 0 0 0-1.5h-3.5Z"
clip-rule="evenodd"
d="M12.2246 7.64453C14.5288 7.76135 16.362 9.66677 16.3623 12C16.3622 13.2041 15.8727 14.2962 15.084 15.085H15.085C14.6803 15.4904 14.1991 15.812 13.6699 16.0313C13.207 16.2229 12.7142 16.3323 12.2148 16.3564L12 16.3623C9.59163 16.3621 7.63795 14.4084 7.6377 12C7.63783 10.796 8.12745 9.70477 8.91602 8.91602C9.32071 8.51054 9.80178 8.18893 10.3311 7.96973C10.8603 7.75061 11.4282 7.63803 12.001 7.63867L12.2246 7.64453ZM12.001 9.16016C10.4319 9.16016 9.15918 10.4328 9.15918 12.002C9.15897 12.3747 9.23238 12.7445 9.375 13.0889C9.51768 13.4331 9.72756 13.7464 9.99121 14.0098L9.99219 14.0107C10.2556 14.2744 10.5688 14.4843 10.9131 14.627C11.2575 14.7695 11.6273 14.843 12 14.8428C13.5683 14.8425 14.8405 13.5704 14.8408 12.002C14.8411 11.6293 14.7675 11.2594 14.625 10.915C14.4823 10.5707 14.2725 10.2566 14.0088 9.99317L13.9883 9.97364C13.7291 9.71908 13.4236 9.51516 13.0879 9.37598C12.7435 9.23333 12.3738 9.15993 12.001 9.16016Z"
fill="#140553"
/>
</svg>
<span class="font-semibold text-gray-700">{gettext("Interactions")}</span>
<span class="font-bold text-sm text-[#140553]">{gettext("Interactions")}</span>
</div>
<div class="flex-1 overflow-y-auto p-3 space-y-2">
<div
:if={length(@interactions) == 0}
class="h-full flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-12 w-12 mb-3"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M4 12h6l-6 8h6" /><path d="M14 4h6l-6 8h6" />
</svg>
<p class="text-sm text-center">
{gettext("No interactions on this slide")}
</p>
</div>
<%= for interaction <- @interactions do %>
<div class="flex items-center justify-between p-3 rounded-lg border border-gray-200 hover:border-gray-300 transition-colors">
<div class="flex items-center gap-x-3 flex-1 min-w-0">
<%= case interaction do %>
<% %Claper.Polls.Poll{} -> %>
<div class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16 8v8m-4-5v5m-4-2v2m-2 4h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
</div>
<div class="min-w-0 flex-1">
<p class="text-xs text-gray-500">{gettext("Poll")}</p>
<p class="text-sm font-medium text-gray-900 truncate">{interaction.title}</p>
</div>
<% %Claper.Forms.Form{} -> %>
<div class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"
/>
</svg>
</div>
<div class="min-w-0 flex-1">
<p class="text-xs text-gray-500">{gettext("Form")}</p>
<p class="text-sm font-medium text-gray-900 truncate">{interaction.title}</p>
</div>
<% %Claper.Embeds.Embed{} -> %>
<div class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M14.25 9.75L16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0020.25 18V6A2.25 2.25 0 0018 3.75H6A2.25 2.25 0 003.75 6v12A2.25 2.25 0 006 20.25z"
/>
</svg>
</div>
<div class="min-w-0 flex-1">
<p class="text-xs text-gray-500">{gettext("Web content")}</p>
<p class="text-sm font-medium text-gray-900 truncate">{interaction.title}</p>
</div>
<% %Claper.Quizzes.Quiz{} -> %>
<div class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
</svg>
</div>
<div class="min-w-0 flex-1">
<p class="text-xs text-gray-500">{gettext("Quiz")}</p>
<p class="text-sm font-medium text-gray-900 truncate">{interaction.title}</p>
</div>
<% _ -> %>
<% end %>
</div>
<div class="flex items-center gap-x-2 ml-2">
<.link
patch={edit_path(@event_code, interaction)}
class="p-1.5 rounded-md hover:bg-gray-100 text-gray-500 hover:text-gray-700 transition-colors"
>
<div
:if={length(@interactions) == 0}
class="flex flex-col items-center justify-center text-gray-400 py-8"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-12 w-12 mb-3"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M4 12h6l-6 8h6" /><path d="M14 4h6l-6 8h6" />
</svg>
<p class="text-sm text-center">
{gettext("No interactions on this slide")}
</p>
</div>
<%= for interaction <- @interactions do %>
<div class={[
"flex items-center gap-2 overflow-hidden pl-2 pr-3 py-2 rounded-xl w-full",
if(interaction.enabled,
do: "bg-gray-100 border-b-2 border-accent",
else: "bg-white border border-gray-200"
)
]}>
<div class={[
"flex items-center justify-center rounded-full w-[42px] h-[41px] shrink-0",
if(interaction.enabled, do: "bg-white", else: "bg-gray-100")
]}>
<%= case interaction do %>
<% %Claper.Polls.Poll{} -> %>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
class="h-5 w-5 text-gray-700"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@@ -148,26 +77,101 @@ defmodule ClaperWeb.EventLive.ManageInteractionListComponent do
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
d="M16 8v8m-4-5v5m-4-2v2m-2 4h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
</.link>
<button
phx-click={toggle_event(interaction)}
phx-value-id={interaction.id}
class={"relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none #{if interaction.enabled, do: "bg-primary-500", else: "bg-gray-200"}"}
>
<span class={"pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out #{if interaction.enabled, do: "translate-x-5", else: "translate-x-0"}"}>
</span>
</button>
</div>
<% %Claper.Forms.Form{} -> %>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-gray-700"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"
/>
</svg>
<% %Claper.Embeds.Embed{} -> %>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="h-5 w-5 text-gray-700"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M14.25 9.75L16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0020.25 18V6A2.25 2.25 0 0018 3.75H6A2.25 2.25 0 003.75 6v12A2.25 2.25 0 006 20.25z"
/>
</svg>
<% %Claper.Quizzes.Quiz{} -> %>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="h-5 w-5 text-gray-700"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
</svg>
<% _ -> %>
<% end %>
</div>
<% end %>
</div>
<div class="flex-1 min-w-0">
<p class="font-bold text-sm text-gray-800 leading-snug truncate">
{interaction.title}
</p>
<p class="text-xs text-gray-500 leading-tight truncate">
{type_label(interaction)}
</p>
</div>
<.link
patch={edit_path(@event_code, interaction)}
class="flex items-center justify-center rounded-full w-[42px] h-[41px] shrink-0 border border-[#140553] text-[#140553]"
>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M3.33301 17.4998H16.6663M4.72134 10.989C4.36602 11.3451 4.16643 11.8276 4.16634 12.3306V14.9998H6.85217C7.35551 14.9998 7.83801 14.7998 8.19384 14.4431L16.1105 6.52229C16.4657 6.16612 16.6652 5.68364 16.6652 5.18063C16.6652 4.67761 16.4657 4.19513 16.1105 3.83896L15.3288 3.05563C15.1526 2.87925 14.9432 2.73935 14.7129 2.64393C14.4825 2.54851 14.2355 2.49943 13.9862 2.49951C13.7368 2.49959 13.4899 2.54882 13.2596 2.64438C13.0292 2.73995 12.82 2.87998 12.6438 3.05646L4.72134 10.989Z"
stroke="#140553"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</.link>
<input
type="checkbox"
class="toggle toggle-sm shrink-0 bg-gray-200 border-gray-300 [--tglbg:white] checked:bg-white checked:border-accent checked:[--tglbg:var(--color-accent)]"
checked={interaction.enabled}
phx-click={toggle_event(interaction)}
phx-value-id={interaction.id}
/>
</div>
<% end %>
</div>
"""
end
defp type_label(%Claper.Polls.Poll{}), do: gettext("Poll")
defp type_label(%Claper.Forms.Form{}), do: gettext("Form")
defp type_label(%Claper.Embeds.Embed{}), do: gettext("Web content")
defp type_label(%Claper.Quizzes.Quiz{}), do: gettext("Quiz")
defp type_label(_), do: ""
defp edit_path(event_code, %Claper.Polls.Poll{id: id}),
do: ~p"/e/#{event_code}/manage/edit/poll/#{id}"

View File

@@ -5,153 +5,158 @@ defmodule ClaperWeb.EventLive.ManageablePostComponent do
assigns = assigns |> assign_new(:readonly, fn -> false end)
~H"""
<div
id={"#{@id}"}
class={"#{if ClaperWeb.Helpers.body_without_links(@post.body) =~ "?", do: "border-supporting-yellow-400 border-2"} flex flex-col md:block px-4 pb-2 pt-3 rounded-b-lg rounded-tr-lg bg-white relative shadow-md text-black break-all mt-2"}
>
<div
:if={ClaperWeb.Helpers.body_without_links(@post.body) =~ "?"}
class="inline-flex items-center space-x-1 justify-center px-3 py-0.5 rounded-full text-xs font-medium bg-supporting-yellow-400 text-white mb-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M15 8A7 7 0 1 1 1 8a7 7 0 0 1 14 0Zm-6 3.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM7.293 5.293a1 1 0 1 1 .99 1.667c-.459.134-1.033.566-1.033 1.29v.25a.75.75 0 1 0 1.5 0v-.115a2.5 2.5 0 1 0-2.518-4.153.75.75 0 1 0 1.061 1.06Z"
clip-rule="evenodd"
/>
</svg>
<span>{gettext("Question")}</span>
</div>
<div :if={!@readonly} class="float-right mr-1">
<%= if @post.attendee_identifier do %>
<span class="text-yellow-500">
{link(
if @post.pinned do
gettext("Unpin")
else
gettext("Pin")
end,
to: "#",
phx_click: "pin",
phx_value_id: @post.uuid,
phx_value_event_id: @event.uuid
)}
</span>
/
<span class="text-red-500">
{link(gettext("Ban"),
to: "#",
phx_click: "ban",
phx_value_attendee_identifier: @post.attendee_identifier,
data: [
confirm:
gettext(
"Blocking this user will delete all his messages and he will not be able to join again, confirm ?"
)
]
)}
</span>
/
<% else %>
<span class="text-yellow-500">
{link(
if @post.pinned do
gettext("Unpin")
else
gettext("Pin")
end,
to: "#",
phx_click: "pin",
phx_value_id: @post.uuid,
phx_value_event_id: @event.uuid
)}
</span>
/
<span class="text-red-500">
{link(gettext("Ban"),
to: "#",
phx_click: "ban",
phx_value_user_id: @post.user_id,
data: [
confirm:
gettext(
"Blocking this user will delete all his messages and he will not be able to join again, confirm ?"
)
]
)}
</span>
/
<% end %>
<span class="text-red-500">
{link(gettext("Delete"),
to: "#",
phx_click: "delete",
phx_value_id: @post.uuid,
phx_value_event_id: @event.uuid
)}
<div id={"#{@id}"} class="flex items-end gap-2 group">
<!-- Left: time + avatar -->
<div class="flex flex-col items-center gap-2 shrink-0">
<span class="text-xs text-gray-400 leading-4">
{Calendar.strftime(@post.inserted_at, "%H:%M")}
</span>
<div class="avatar avatar-placeholder">
<div class="bg-neutral text-neutral-content w-10 rounded-full">
<%= if @post.name do %>
<span class="text-sm font-medium uppercase">{String.first(@post.name)}</span>
<% end %>
</div>
</div>
</div>
<div class="flex space-x-3 items-center">
<%= if @post.attendee_identifier do %>
<img
class="h-8 w-8"
src={"https://api.dicebear.com/7.x/personas/svg?seed=#{@post.attendee_identifier}"}
/>
<% else %>
<img
class="h-8 w-8"
src={"https://api.dicebear.com/7.x/personas/svg?seed=#{@post.user_id}"}
/>
<% end %>
<div class="flex flex-col">
<%= if @post.name do %>
<p class="text-black text-sm font-semibold mr-2">
{@post.name}
</p>
<% end %>
<p class="text-xl">
{ClaperWeb.Helpers.format_body(@post.body)}
<!-- Right: header + bubble -->
<div class="flex-1 min-w-0 flex flex-col">
<!-- Header: name + question badge + actions -->
<div class="flex items-center gap-1 min-h-6 mb-1 ml-4">
<p :if={@post.name} class="font-bold text-xs text-gray-700 truncate">
{@post.name}
</p>
<div
:if={ClaperWeb.Helpers.body_without_links(@post.body) =~ "?"}
class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-supporting-yellow-400 text-white shrink-0"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-3 h-3"
>
<path
fill-rule="evenodd"
d="M15 8A7 7 0 1 1 1 8a7 7 0 0 1 14 0Zm-6 3.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM7.293 5.293a1 1 0 1 1 .99 1.667c-.459.134-1.033.566-1.033 1.29v.25a.75.75 0 1 0 1.5 0v-.115a2.5 2.5 0 1 0-2.518-4.153.75.75 0 1 0 1.061 1.06Z"
clip-rule="evenodd"
/>
</svg>
<span>{gettext("Question")}</span>
</div>
<!-- Actions (visible on hover) -->
<div
:if={!@readonly}
class="ml-auto flex items-center divide-x divide-gray-200 border border-gray-200 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity shrink-0"
>
<button
class="flex items-center justify-center w-8 h-6"
phx-click="pin"
phx-value-id={@post.uuid}
phx-value-event_id={@event.uuid}
title={if @post.pinned, do: gettext("Unpin"), else: gettext("Pin")}
>
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 13 13" fill="none">
<path
d="M0.5 12.5006L3.58667 9.41327M3.59 9.40994L1.73667 7.5566C1.10067 6.92127 1.74067 5.55927 2.61 5.5046C3.39533 5.4546 5.21333 5.73927 5.818 5.1346L7.478 3.4746C7.88933 3.0626 7.628 2.14127 7.60133 1.63327C7.56267 0.955937 8.64 0.11927 9.21133 0.690603L12.3093 3.78927C12.8827 4.36127 12.0427 5.43527 11.3673 5.39927C10.8593 5.3726 9.93733 5.11127 9.52533 5.5226L7.86533 7.1826C7.26133 7.78727 7.54533 9.6046 7.496 10.3899C7.44133 11.2599 6.07933 11.8999 5.44267 11.2633L3.59 9.40994Z"
stroke="#140553"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
<%= if @post.attendee_identifier do %>
<button
class="flex items-center justify-center w-8 h-6"
phx-click="ban"
phx-value-attendee_identifier={@post.attendee_identifier}
data-confirm={gettext("Blocking this user will delete all his messages and he will not be able to join again, confirm ?")}
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
<path
d="M6.611 0C5.30347 0 4.0253 0.387729 2.93813 1.11415C1.85095 1.84058 1.00361 2.87308 0.503235 4.08108C0.00286441 5.28908 -0.128055 6.61833 0.127031 7.90074C0.382118 9.18315 1.01175 10.3611 1.93632 11.2857C2.86088 12.2102 4.03885 12.8399 5.32126 13.095C6.60367 13.3501 7.93292 13.2191 9.14092 12.7188C10.3489 12.2184 11.3814 11.371 12.1078 10.2839C12.8343 9.1967 13.222 7.91853 13.222 6.611C13.222 4.85765 12.5255 3.17612 11.2857 1.93632C10.0459 0.696514 8.36435 0 6.611 0ZM1.10184 6.611C1.10073 5.34054 1.54228 4.10938 2.35058 3.12921L10.0928 10.8714C9.28666 11.5326 8.30923 11.9512 7.27439 12.0783C6.23955 12.2054 5.18989 12.0358 4.2477 11.5894C3.30551 11.1429 2.50957 10.4379 1.95261 9.55652C1.39566 8.67513 1.10061 7.65362 1.10184 6.611ZM10.8714 10.0928L3.12921 2.35058C4.18518 1.48642 5.52459 1.04549 6.88739 1.11339C8.25019 1.18129 9.53914 1.75318 10.504 2.71802C11.4688 3.68286 12.0407 4.97181 12.1086 6.33461C12.1765 7.69741 11.7356 9.03682 10.8714 10.0928Z"
fill="#E14640"
/>
</svg>
</button>
<% else %>
<button
class="flex items-center justify-center w-8 h-6"
phx-click="ban"
phx-value-user_id={@post.user_id}
data-confirm={gettext("Blocking this user will delete all his messages and he will not be able to join again, confirm ?")}
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
<path
d="M6.611 0C5.30347 0 4.0253 0.387729 2.93813 1.11415C1.85095 1.84058 1.00361 2.87308 0.503235 4.08108C0.00286441 5.28908 -0.128055 6.61833 0.127031 7.90074C0.382118 9.18315 1.01175 10.3611 1.93632 11.2857C2.86088 12.2102 4.03885 12.8399 5.32126 13.095C6.60367 13.3501 7.93292 13.2191 9.14092 12.7188C10.3489 12.2184 11.3814 11.371 12.1078 10.2839C12.8343 9.1967 13.222 7.91853 13.222 6.611C13.222 4.85765 12.5255 3.17612 11.2857 1.93632C10.0459 0.696514 8.36435 0 6.611 0ZM1.10184 6.611C1.10073 5.34054 1.54228 4.10938 2.35058 3.12921L10.0928 10.8714C9.28666 11.5326 8.30923 11.9512 7.27439 12.0783C6.23955 12.2054 5.18989 12.0358 4.2477 11.5894C3.30551 11.1429 2.50957 10.4379 1.95261 9.55652C1.39566 8.67513 1.10061 7.65362 1.10184 6.611ZM10.8714 10.0928L3.12921 2.35058C4.18518 1.48642 5.52459 1.04549 6.88739 1.11339C8.25019 1.18129 9.53914 1.75318 10.504 2.71802C11.4688 3.68286 12.0407 4.97181 12.1086 6.33461C12.1765 7.69741 11.7356 9.03682 10.8714 10.0928Z"
fill="#E14640"
/>
</svg>
</button>
<% end %>
<button
class="flex items-center justify-center w-8 h-6"
phx-click="delete"
phx-value-id={@post.uuid}
phx-value-event_id={@event.uuid}
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M6.66683 3.33333H9.3335C9.3335 2.97971 9.19302 2.64057 8.94297 2.39052C8.69292 2.14048 8.35379 2 8.00016 2C7.64654 2 7.3074 2.14048 7.05735 2.39052C6.80731 2.64057 6.66683 2.97971 6.66683 3.33333ZM5.66683 3.33333C5.66683 3.02692 5.72718 2.7235 5.84444 2.44041C5.9617 2.15731 6.13358 1.90009 6.35025 1.68342C6.56692 1.46675 6.82414 1.29488 7.10723 1.17761C7.39033 1.06035 7.69375 1 8.00016 1C8.30658 1 8.61 1.06035 8.89309 1.17761C9.17618 1.29488 9.43341 1.46675 9.65008 1.68342C9.86675 1.90009 10.0386 2.15731 10.1559 2.44041C10.2731 2.7235 10.3335 3.02692 10.3335 3.33333H14.1668C14.2994 3.33333 14.4266 3.38601 14.5204 3.47978C14.6142 3.57355 14.6668 3.70073 14.6668 3.83333C14.6668 3.96594 14.6142 4.09312 14.5204 4.18689C14.4266 4.28066 14.2994 4.33333 14.1668 4.33333H13.2868L12.5068 12.4073C12.447 13.026 12.1589 13.6002 11.6986 14.0179C11.2384 14.4356 10.639 14.6669 10.0175 14.6667H5.98283C5.36141 14.6667 4.76223 14.4354 4.30213 14.0177C3.84203 13.6 3.55399 13.0259 3.49416 12.4073L2.7135 4.33333H1.8335C1.70089 4.33333 1.57371 4.28066 1.47994 4.18689C1.38617 4.09312 1.3335 3.96594 1.3335 3.83333C1.3335 3.70073 1.38617 3.57355 1.47994 3.47978C1.57371 3.38601 1.70089 3.33333 1.8335 3.33333H5.66683ZM7.00016 6.5C7.00016 6.36739 6.94748 6.24022 6.85372 6.14645C6.75995 6.05268 6.63277 6 6.50016 6C6.36755 6 6.24038 6.05268 6.14661 6.14645C6.05284 6.24022 6.00016 6.36739 6.00016 6.5V11.5C6.00016 11.6326 6.05284 11.7598 6.14661 11.8536C6.24038 11.9473 6.36755 12 6.50016 12C6.63277 12 6.75995 11.9473 6.85372 11.8536C6.94748 11.7598 7.00016 11.6326 7.00016 11.5V6.5ZM9.50016 6C9.63277 6 9.75995 6.05268 9.85372 6.14645C9.94748 6.24022 10.0002 6.36739 10.0002 6.5V11.5C10.0002 11.6326 9.94748 11.7598 9.85372 11.8536C9.75995 11.9473 9.63277 12 9.50016 12C9.36755 12 9.24038 11.9473 9.14661 11.8536C9.05284 11.7598 9.00016 11.6326 9.00016 11.5V6.5C9.00016 6.36739 9.05284 6.24022 9.14661 6.14645C9.24038 6.05268 9.36755 6 9.50016 6ZM4.4895 12.3113C4.52545 12.6824 4.69833 13.0268 4.97441 13.2774C5.25049 13.528 5.60999 13.6667 5.98283 13.6667H10.0175C10.3903 13.6667 10.7498 13.528 11.0259 13.2774C11.302 13.0268 11.4749 12.6824 11.5108 12.3113L12.2828 4.33333H3.7175L4.4895 12.3113Z"
fill="#7F1D1D"
/>
</svg>
</button>
</div>
</div>
<!-- Message bubble with tail -->
<div class="flex items-end">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
class="shrink-0 -mr-px"
>
<path d="M0 16H16V0C16 5.33333 5.33333 16 0 16Z" fill="white" />
</svg>
<div class="bg-white rounded-tl-2xl rounded-tr-2xl rounded-br-2xl flex-1 min-w-0 px-3 py-4">
<p class="text-sm text-gray-700 break-words">
{ClaperWeb.Helpers.format_body(@post.body)}
</p>
<%= if @post.like_count > 0 || @post.love_count > 0 || @post.lol_count > 0 do %>
<div class="flex items-center gap-1 mt-2 text-gray-700">
<div
:if={@post.like_count > 0}
class="border border-gray-200 rounded-full px-2 py-2 flex items-center gap-1"
>
<span class="text-sm leading-none">👍</span>
<span class="font-bold text-xs">{@post.like_count}</span>
</div>
<div
:if={@post.love_count > 0}
class="border border-gray-200 rounded-full px-2 py-2 flex items-center gap-1"
>
<span class="text-sm leading-none">❤️</span>
<span class="font-bold text-xs">{@post.love_count}</span>
</div>
<div
:if={@post.lol_count > 0}
class="border border-gray-200 rounded-full px-2 py-2 flex items-center gap-1"
>
<span class="text-sm leading-none">😂</span>
<span class="font-bold text-xs">{@post.lol_count}</span>
</div>
</div>
<% end %>
</div>
</div>
</div>
<%= if @post.like_count> 0 || @post.love_count > 0 || @post.lol_count > 0 do %>
<div class="flex h-6 space-x-2 text-base text-gray-500 pb-3 items-center mt-5">
<div class="flex items-center">
<%= if @post.like_count> 0 do %>
<img src="/images/icons/thumb.svg" class="h-4" />
<span class="ml-1">
{@post.like_count}
</span>
<% end %>
</div>
<div class="flex items-center">
<%= if @post.love_count> 0 do %>
<img src="/images/icons/heart.svg" class="h-4" />
<span class="ml-1">
{@post.love_count}
</span>
<% end %>
</div>
<div class="flex items-center">
<%= if @post.lol_count> 0 do %>
<img src="/images/icons/laugh.svg" class="h-4" />
<span class="ml-1">
{@post.lol_count}
</span>
<% end %>
</div>
</div>
<% end %>
</div>
"""
end