mirror of
https://github.com/ClaperCo/Claper.git
synced 2026-08-29 10:09:08 +02:00
588 lines
22 KiB
Plaintext
588 lines
22 KiB
Plaintext
<%= if @started || @is_leader do %>
|
|
<% focus_content? =
|
|
not is_nil(@current_slide_url) ||
|
|
match?(%Claper.Polls.Poll{}, @current_interaction) ||
|
|
match?(%Claper.Forms.Form{}, @current_interaction) ||
|
|
match?(%Claper.Quizzes.Quiz{}, @current_interaction) ||
|
|
match?(%Claper.Embeds.Embed{attendee_visibility: true}, @current_interaction) %>
|
|
<% interaction_mode? =
|
|
match?(%Claper.Polls.Poll{}, @current_interaction) ||
|
|
match?(%Claper.Forms.Form{}, @current_interaction) ||
|
|
match?(%Claper.Quizzes.Quiz{}, @current_interaction) ||
|
|
match?(%Claper.Embeds.Embed{attendee_visibility: true}, @current_interaction) %>
|
|
<div class="h-[100dvh] overflow-hidden bg-black lg:bg-primary-700">
|
|
<main
|
|
id="attendee-room"
|
|
class="relative mx-auto grid h-[100dvh] w-full max-w-lg grid-rows-[auto_auto_minmax(0,1fr)] overflow-hidden bg-black font-display shadow-2xl"
|
|
>
|
|
<div
|
|
id="side-menu-shadow"
|
|
phx-click={toggle_side_menu()}
|
|
class="fixed inset-0 z-50 hidden bg-black/70"
|
|
>
|
|
</div>
|
|
|
|
<aside
|
|
id="side-menu"
|
|
class="fixed inset-y-0 left-0 z-[60] hidden w-64 flex-col rounded-r-2xl bg-white px-4 text-gray-900 shadow-2xl animate__faster"
|
|
>
|
|
<img src="/images/logo-large-black.svg" class="my-3 h-16" alt="Claper" />
|
|
<span class="text-xl font-bold">{@event.name}</span>
|
|
<a
|
|
class="mt-5 flex items-center rounded-lg bg-gray-200 px-3 py-2"
|
|
href={~p"/?disconnected_from=#{@event.uuid}"}
|
|
>
|
|
<img src="/images/icons/exit-outline.svg" class="mr-3 h-5" />
|
|
<span>{gettext("Leave")}</span>
|
|
</a>
|
|
</aside>
|
|
|
|
<div
|
|
id="connection-status"
|
|
class="pointer-events-none absolute inset-x-4 top-16 z-50 hidden items-center justify-center rounded-full bg-supporting-yellow-100 px-4 py-2 text-sm font-semibold text-supporting-yellow-900 shadow-lg"
|
|
>
|
|
{gettext("Reconnecting...")}
|
|
</div>
|
|
|
|
<div
|
|
id="identity-menu"
|
|
phx-click-away={JS.hide(to: "#identity-menu")}
|
|
class="absolute bottom-16 left-2 z-40 hidden w-64 rounded-2xl border border-white/10 bg-gray-900 p-2 text-white shadow-2xl"
|
|
>
|
|
<p class="px-3 pb-2 pt-1 text-xs font-semibold uppercase tracking-wide text-gray-400">
|
|
{gettext("Post as")}
|
|
</p>
|
|
<button
|
|
:if={@state.anonymous_chat_enabled}
|
|
id="setAnonymous"
|
|
type="button"
|
|
phx-click={JS.push("set-nickname") |> JS.hide(to: "#identity-menu")}
|
|
phx-value-nickname=""
|
|
phx-hook="EmptyNickname"
|
|
data-storage-key={"nickname:#{@event.uuid}"}
|
|
class="flex min-h-11 w-full items-center gap-3 rounded-xl px-3 py-2 text-left hover:bg-white/10"
|
|
>
|
|
<span class="grid h-8 w-8 place-items-center rounded-full bg-gray-700">?</span>
|
|
<span class="font-semibold">{gettext("Anonymous")}</span>
|
|
</button>
|
|
<button
|
|
:if={!@state.anonymous_chat_enabled}
|
|
type="button"
|
|
disabled
|
|
class="flex min-h-11 w-full cursor-not-allowed items-center gap-3 rounded-xl px-3 py-2 text-left opacity-40"
|
|
>
|
|
<span class="grid h-8 w-8 place-items-center rounded-full bg-gray-700">?</span>
|
|
<span class="font-semibold">{gettext("Anonymous")} ({gettext("disabled")})</span>
|
|
</button>
|
|
<button
|
|
id="nicknamepicker"
|
|
type="button"
|
|
data-prompt={gettext("Enter your name")}
|
|
data-invalid={gettext("Nickname must be between 2 and 20 characters")}
|
|
data-close={JS.hide(to: "#identity-menu")}
|
|
data-storage-key={"nickname:#{@event.uuid}"}
|
|
phx-hook="NicknamePicker"
|
|
class="flex min-h-11 w-full items-center gap-3 rounded-xl px-3 py-2 text-left hover:bg-white/10"
|
|
>
|
|
<span class="grid h-8 w-8 place-items-center rounded-full bg-primary-500">
|
|
{if @nickname in [nil, ""], do: "A", else: String.first(@nickname)}
|
|
</span>
|
|
<span class="min-w-0">
|
|
<span class="block text-xs text-gray-400">{gettext("Nickname")}</span>
|
|
<span class="block truncate font-semibold">
|
|
{if @nickname in [nil, ""], do: gettext("Set your nickname"), else: @nickname}
|
|
</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
|
|
<header
|
|
id="room-topbar"
|
|
class="relative z-30 bg-black px-4 pb-3 pt-[max(0.75rem,env(safe-area-inset-top))]"
|
|
>
|
|
<div id="banner" class="hidden w-full pb-2 text-center" phx-hook="EmbeddedBanner">
|
|
<a href="https://claper.co" target="_blank" class="text-xs text-gray-400">
|
|
{gettext("Create your next presentation with")} <span class="underline">Claper</span>
|
|
</a>
|
|
</div>
|
|
<div class="flex items-center justify-between gap-3">
|
|
<button
|
|
type="button"
|
|
phx-click={toggle_side_menu()}
|
|
class="inline-flex min-h-11 items-center gap-2 rounded-full bg-primary-500 px-4 text-sm font-bold uppercase text-white shadow-lg shadow-primary-900/30"
|
|
>
|
|
<img src="/images/icons/menu-outline.svg" class="h-5 w-5" />
|
|
<span>#{@event.code}</span>
|
|
</button>
|
|
|
|
<div class="ml-auto inline-flex min-h-11 items-center gap-2 text-sm font-semibold text-white">
|
|
<img src="/images/icons/online-users.svg" class="h-5 w-5" />
|
|
<span id="counter" phx-update="ignore" phx-hook="UpdateAttendees">
|
|
{@attendees_nb}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<section
|
|
id="focus-slot"
|
|
phx-hook="AttendeeFocus"
|
|
data-focus-key={focus_key(@current_interaction, @state.position)}
|
|
data-collapse-key={"focus-slot:#{@event.uuid}"}
|
|
data-interaction-mode={to_string(interaction_mode?)}
|
|
class={[
|
|
"relative z-10 min-h-0 overflow-hidden border-y border-white/10 bg-gray-950 transition-[height] duration-300",
|
|
focus_content? && interaction_mode? && "h-auto max-h-[40dvh]",
|
|
focus_content? && !interaction_mode? && "h-[40dvh] min-h-40 max-h-[26rem]",
|
|
!focus_content? && "h-12"
|
|
]}
|
|
>
|
|
<div
|
|
id="new-interaction-badge"
|
|
class="pointer-events-none absolute left-1/2 top-3 z-30 hidden -translate-x-1/2 rounded-full bg-primary-500 px-3 py-1 text-xs font-bold text-white shadow-lg"
|
|
>
|
|
{gettext("New interaction")}
|
|
</div>
|
|
|
|
<%= case @current_interaction do %>
|
|
<% %Claper.Polls.Poll{} -> %>
|
|
<div class="max-h-[40dvh] overflow-y-auto overscroll-contain p-3">
|
|
<.live_component
|
|
module={ClaperWeb.EventLive.PollComponent}
|
|
id={"#{@current_interaction.id}-poll"}
|
|
poll={@current_interaction}
|
|
current_user={@current_user}
|
|
attendee_identifier={@attendee_identifier}
|
|
event={@event}
|
|
selected_poll_opt={@selected_poll_opt}
|
|
current_poll_vote={@current_poll_vote}
|
|
show_results={@current_interaction.show_results}
|
|
focus_mode
|
|
/>
|
|
</div>
|
|
<% %Claper.Forms.Form{} -> %>
|
|
<div class="max-h-[40dvh] overflow-y-auto overscroll-contain p-3">
|
|
<.live_component
|
|
module={ClaperWeb.EventLive.FormComponent}
|
|
id={"#{@current_interaction.id}-form"}
|
|
form={@current_interaction}
|
|
current_user={@current_user}
|
|
attendee_identifier={@attendee_identifier}
|
|
event={@event}
|
|
current_form_submit={@current_form_submit}
|
|
focus_mode
|
|
/>
|
|
</div>
|
|
<% %Claper.Quizzes.Quiz{} -> %>
|
|
<div class="max-h-[40dvh] overflow-y-auto overscroll-contain p-3">
|
|
<.live_component
|
|
module={ClaperWeb.EventLive.QuizComponent}
|
|
id={"#{@current_interaction.id}-quiz"}
|
|
quiz={@current_interaction}
|
|
current_user={@current_user}
|
|
attendee_identifier={@attendee_identifier}
|
|
event={@event}
|
|
selected_quiz_question_opts={@selected_quiz_question_opts}
|
|
current_quiz_question_idx={@current_quiz_question_idx}
|
|
current_quiz_responses={@current_quiz_responses}
|
|
quiz_score={@quiz_score}
|
|
focus_mode
|
|
/>
|
|
</div>
|
|
<% %Claper.Embeds.Embed{attendee_visibility: true} -> %>
|
|
<div class="max-h-[40dvh] overflow-y-auto overscroll-contain p-3">
|
|
<.live_component
|
|
id={"#{@current_interaction.id}-embed"}
|
|
module={ClaperWeb.EventLive.EmbedComponent}
|
|
embed={@current_interaction}
|
|
focus_mode
|
|
/>
|
|
</div>
|
|
<% _ -> %>
|
|
<%= if @current_slide_url do %>
|
|
<div id="focus-media" class="relative h-full w-full bg-black">
|
|
<img
|
|
src={@current_slide_url}
|
|
alt={gettext("Current presentation slide")}
|
|
class="h-full w-full object-contain"
|
|
/>
|
|
</div>
|
|
<% else %>
|
|
<div class="flex h-full items-center justify-between gap-3 px-4 text-sm text-gray-400">
|
|
<span>{gettext("Waiting for content")}</span>
|
|
<span class="h-2 w-2 animate-pulse rounded-full bg-primary-400"></span>
|
|
</div>
|
|
<% end %>
|
|
<% end %>
|
|
|
|
<button
|
|
:if={focus_content? && !interaction_mode?}
|
|
type="button"
|
|
data-focus-fullscreen
|
|
aria-label={gettext("Open fullscreen")}
|
|
class="absolute right-3 top-3 z-20 grid h-11 w-11 place-items-center rounded-full bg-black/65 text-white backdrop-blur"
|
|
>
|
|
<svg
|
|
class="h-5 w-5"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
d="M8 3H3v5M16 3h5v5M8 21H3v-5m13 5h5v-5"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
|
|
<button
|
|
:if={focus_content? && !interaction_mode?}
|
|
type="button"
|
|
data-focus-collapse
|
|
aria-label={gettext("Hide presentation")}
|
|
class="absolute right-16 top-3 z-20 grid h-11 w-11 place-items-center rounded-full bg-black/65 text-white backdrop-blur"
|
|
>
|
|
<svg
|
|
class="h-5 w-5"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="m6 15 6-6 6 6" />
|
|
</svg>
|
|
</button>
|
|
|
|
<div
|
|
:if={focus_content? && !interaction_mode?}
|
|
data-focus-collapsed-bar
|
|
class="hidden h-full items-center px-4"
|
|
>
|
|
<button
|
|
type="button"
|
|
data-focus-show
|
|
class="flex min-h-11 w-full items-center justify-between text-sm font-semibold text-white"
|
|
>
|
|
<span>{gettext("Show presentation")}</span>
|
|
<svg
|
|
class="h-5 w-5"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
>
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="m6 9 6 6 6-6" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div
|
|
:if={
|
|
!interaction_mode? && @transcription_config && @transcription_config.enabled &&
|
|
@transcription_config.visibility in ["both", "attendee"]
|
|
}
|
|
id="focus-captions"
|
|
class="absolute inset-x-0 bottom-0 z-20"
|
|
>
|
|
<button
|
|
type="button"
|
|
data-caption-toggle
|
|
aria-label={gettext("Toggle captions")}
|
|
class="absolute bottom-2 right-2 grid h-11 w-11 place-items-center rounded-full bg-black/70 text-xs font-black text-white backdrop-blur"
|
|
>
|
|
CC
|
|
</button>
|
|
<div
|
|
data-caption-text
|
|
class={[
|
|
"mx-14 mb-2 rounded-lg bg-black/80 px-3 py-2 text-center text-sm font-medium text-white backdrop-blur",
|
|
@transcription_text == "" && "hidden"
|
|
]}
|
|
>
|
|
{@transcription_text}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="chat-panel" class="relative min-h-0 overflow-visible bg-black">
|
|
<div
|
|
id="chat-feed"
|
|
class={[
|
|
"h-full space-y-3 overflow-y-auto overscroll-contain px-4 pt-4",
|
|
@state.message_reaction_enabled && "pb-40",
|
|
!@state.message_reaction_enabled && "pb-24"
|
|
]}
|
|
data-room-reactions={to_string(@state.message_reaction_enabled)}
|
|
phx-update="stream"
|
|
phx-hook="RoomFeed"
|
|
data-chip="#new-messages-chip"
|
|
data-posts-nb={@post_count}
|
|
>
|
|
<.live_component
|
|
:for={{id, post} <- @streams.posts}
|
|
module={ClaperWeb.EventLive.PostComponent}
|
|
id={id}
|
|
post={post}
|
|
leaders={@leaders}
|
|
is_leader={@is_leader}
|
|
current_user={@current_user}
|
|
attendee_identifier={@attendee_identifier}
|
|
event={@event}
|
|
reaction_enabled={@state.message_reaction_enabled}
|
|
liked_posts={@like_posts}
|
|
loved_posts={@love_posts}
|
|
loled_posts={@lol_posts}
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
:if={@post_count == 0 && @state.chat_enabled}
|
|
class="pointer-events-none absolute inset-0 flex items-center justify-center px-8 text-center text-sm text-gray-500"
|
|
>
|
|
{gettext("Be the first to ask a question or share a thought.")}
|
|
</div>
|
|
|
|
<button
|
|
id="new-messages-chip"
|
|
type="button"
|
|
class="absolute bottom-20 left-1/2 z-20 hidden -translate-x-1/2 rounded-full bg-primary-500 px-4 py-2 text-xs font-bold text-white shadow-xl"
|
|
>
|
|
<span data-unread-count></span> {gettext("new messages")} ↓
|
|
</button>
|
|
|
|
<div
|
|
id="reacts"
|
|
phx-hook="GlobalReacts"
|
|
data-class-name="h-12 w-12"
|
|
class="pointer-events-none absolute inset-0 z-10 overflow-hidden"
|
|
phx-update="ignore"
|
|
>
|
|
</div>
|
|
|
|
<div
|
|
:if={@state.message_reaction_enabled}
|
|
id="room-reaction-fab"
|
|
phx-hook="RoomReactionFab"
|
|
class="absolute bottom-20 left-4 z-20 flex w-14 flex-col items-center gap-2"
|
|
>
|
|
<div
|
|
data-reaction-picker
|
|
role="menu"
|
|
class="hidden flex-col-reverse gap-2 rounded-full bg-gray-900/95 p-2 shadow-2xl"
|
|
>
|
|
<button
|
|
type="button"
|
|
data-reaction="heart"
|
|
role="menuitem"
|
|
aria-label={gettext("Heart")}
|
|
class="grid h-11 w-11 place-items-center rounded-full hover:bg-white/10"
|
|
>
|
|
<img
|
|
src="/images/icons/heart.svg"
|
|
draggable="false"
|
|
class="pointer-events-none h-6 w-6 select-none"
|
|
/>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
data-reaction="clap"
|
|
role="menuitem"
|
|
aria-label={gettext("Clap")}
|
|
class="grid h-11 w-11 place-items-center rounded-full hover:bg-white/10"
|
|
>
|
|
<img
|
|
src="/images/icons/clap.svg"
|
|
draggable="false"
|
|
class="pointer-events-none h-6 w-6 select-none"
|
|
/>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
data-reaction="hundred"
|
|
role="menuitem"
|
|
aria-label={gettext("Hundred")}
|
|
class="grid h-11 w-11 place-items-center rounded-full hover:bg-white/10"
|
|
>
|
|
<img
|
|
src="/images/icons/hundred.svg"
|
|
draggable="false"
|
|
class="pointer-events-none h-6 w-6 select-none"
|
|
/>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
data-reaction="raisehand"
|
|
role="menuitem"
|
|
aria-label={gettext("Raise hand")}
|
|
class="grid h-11 w-11 place-items-center rounded-full hover:bg-white/10"
|
|
>
|
|
<img
|
|
src="/images/icons/raisehand.svg"
|
|
draggable="false"
|
|
class="pointer-events-none h-6 w-6 select-none"
|
|
/>
|
|
</button>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
data-reaction-trigger
|
|
aria-label={gettext("Send a live reaction")}
|
|
aria-haspopup="menu"
|
|
aria-expanded="false"
|
|
class="grid h-14 w-14 place-items-center rounded-full border-4 border-gray-900 bg-supporting-yellow-100 shadow-xl"
|
|
>
|
|
<img
|
|
data-reaction-icon
|
|
src="/images/icons/heart.svg"
|
|
draggable="false"
|
|
class="pointer-events-none h-7 w-7 select-none"
|
|
/>
|
|
</button>
|
|
</div>
|
|
</section>
|
|
|
|
<footer
|
|
id="room-composer"
|
|
class="absolute inset-x-0 bottom-0 z-30 bg-transparent px-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] pt-2"
|
|
>
|
|
<%= if @state.chat_enabled do %>
|
|
<.form
|
|
:let={f}
|
|
for={@post_changeset}
|
|
id="post-form"
|
|
class="attendee-composer flex items-center gap-1.5 rounded-2xl border border-white/20 px-1.5 py-1 shadow-2xl"
|
|
phx-hook="PostForm"
|
|
data-nickname={@nickname}
|
|
data-storage-key={"post-draft:#{@event.uuid}"}
|
|
phx-submit="save"
|
|
>
|
|
<button
|
|
id="composer-identity-button"
|
|
type="button"
|
|
aria-label={gettext("Choose identity")}
|
|
phx-click={JS.toggle(to: "#identity-menu")}
|
|
class="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-black/60 text-xs font-bold text-white"
|
|
>
|
|
{if @nickname in [nil, ""], do: "A", else: String.first(@nickname)}
|
|
</button>
|
|
|
|
{textarea(f, :body,
|
|
id: "postFormTA",
|
|
disabled: !@state.anonymous_chat_enabled && @nickname in [nil, ""],
|
|
rows: 1,
|
|
maxlength: 255,
|
|
class:
|
|
"min-h-9 max-h-16 min-w-0 flex-1 resize-none border-0 bg-transparent px-2 py-2 text-base leading-5 text-white outline-hidden placeholder:text-white/70 focus:ring-0 disabled:cursor-not-allowed disabled:opacity-50",
|
|
placeholder:
|
|
if(!@state.anonymous_chat_enabled && @nickname in [nil, ""],
|
|
do: gettext("Set your nickname to participate"),
|
|
else: gettext("Ask, comment...")
|
|
)
|
|
)}
|
|
|
|
<button
|
|
type="submit"
|
|
id="submitBtn"
|
|
disabled
|
|
aria-label={gettext("Send message")}
|
|
class="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-black/20 text-white opacity-50 transition disabled:cursor-not-allowed"
|
|
>
|
|
<img src="/images/icons/send.svg" class="h-5 w-5" />
|
|
</button>
|
|
</.form>
|
|
<% else %>
|
|
<div class="attendee-composer flex min-h-10 items-center rounded-2xl border border-white/20 px-3 text-sm text-white/60 shadow-2xl">
|
|
{gettext("Messages deactivated")}
|
|
</div>
|
|
<% end %>
|
|
</footer>
|
|
</main>
|
|
</div>
|
|
<% else %>
|
|
<div class="ticket-stage">
|
|
<div class="ticket" id="waiting-ticket" phx-hook="TicketTilt">
|
|
<div class="texture"></div>
|
|
<div class="glare"></div>
|
|
|
|
<div class="ticket-body">
|
|
<span class="eyebrow">
|
|
<span class="dot"></span>{gettext("Live interactive event")}
|
|
</span>
|
|
|
|
<h1 class="event-title">{@event.name}</h1>
|
|
|
|
<p
|
|
class="event-date"
|
|
x-data={"{date: moment.utc('#{@event.started_at}').local().format('LLLL')}"}
|
|
x-text="date.charAt(0).toUpperCase() + date.slice(1)"
|
|
>
|
|
</p>
|
|
|
|
<div class="cd-row">
|
|
<div class="cd-cell">
|
|
<div class="cd-num">{if @remaining_days < 10, do: "0"}{@remaining_days}</div>
|
|
<div class="cd-label">{gettext("days")}</div>
|
|
</div>
|
|
<div class="cd-sep">:</div>
|
|
<div class="cd-cell">
|
|
<div class="cd-num">{if @remaining_hours < 10, do: "0"}{@remaining_hours}</div>
|
|
<div class="cd-label">{gettext("hours")}</div>
|
|
</div>
|
|
<div class="cd-sep">:</div>
|
|
<div class="cd-cell">
|
|
<div class="cd-num">{if @remaining_minutes < 10, do: "0"}{@remaining_minutes}</div>
|
|
<div class="cd-label">{gettext("minutes")}</div>
|
|
</div>
|
|
<div class="cd-sep">:</div>
|
|
<div class="cd-cell">
|
|
<div class="cd-num">{if @remaining_seconds < 10, do: "0"}{@remaining_seconds}</div>
|
|
<div class="cd-label">{gettext("seconds")}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket-meta">
|
|
<div class="meta-item">
|
|
<span class="meta-k">{gettext("Admit")}</span>
|
|
<span class="meta-v">{gettext("Unlimited")}</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span class="meta-k">{gettext("Session")}</span>
|
|
<span class="meta-v">{@event.code}</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<span class="meta-k">{gettext("No.")}</span>
|
|
<span class="meta-v">CLP-{Calendar.strftime(@event.started_at, "%m%d-%y")}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="perf">
|
|
<div class="notch-bottom"></div>
|
|
</div>
|
|
|
|
<div class="ticket-stub">
|
|
<div class="stub-head">{gettext("Scan to interact in real-time")}</div>
|
|
<div class="qr-frame">
|
|
<div
|
|
phx-hook="QRCode"
|
|
data-code={@event.code}
|
|
data-size="188"
|
|
id="qr"
|
|
phx-update="ignore"
|
|
>
|
|
</div>
|
|
</div>
|
|
<div class="code-label">{gettext("Or use the code:")}</div>
|
|
<div class="code-chip">{@event.code}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<a href="https://claper.co" target="_blank" class="powered-by">
|
|
<span>{gettext("Powered by")}</span>
|
|
<img src="/images/logo-large.svg" alt="Claper" />
|
|
</a>
|
|
</div>
|
|
<% end %>
|