Use cart with barcode scanner
This commit is contained in:
@@ -14,49 +14,134 @@ defmodule MusicLibraryWeb.Components.BarcodeScanner do
|
|||||||
{:ok,
|
{:ok,
|
||||||
socket
|
socket
|
||||||
|> assign(:camera, :pending)
|
|> assign(:camera, :pending)
|
||||||
|> assign(:scan_results, [])}
|
|> assign(:scan_results, [])
|
||||||
|
|> assign(:cart_expanded?, true)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div class="min-w-72" id="barcode-scanner" phx-hook=".BarcodeScanner" phx-target={@myself}>
|
<div id="barcode-scanner" phx-hook=".BarcodeScanner" phx-target={@myself}>
|
||||||
<header>
|
<div class="grid grid-cols-1 md:grid-cols-5">
|
||||||
<h1 class="text-sm/6 font-medium text-zinc-700 dark:text-zinc-400">
|
<section class="md:col-span-3 md:p-4 md:border-r md:border-zinc-200 md:dark:border-zinc-800">
|
||||||
{gettext("Scan one or more barcodes")}
|
<header>
|
||||||
</h1>
|
<h1 class="text-sm/6 font-medium text-zinc-700 dark:text-zinc-400">
|
||||||
</header>
|
{gettext("Scan one or more barcodes")}
|
||||||
<div class="mt-4">
|
</h1>
|
||||||
<.camera_button camera={@camera} />
|
</header>
|
||||||
<video :if={!(@camera == :denied)} class="hidden h-96 w-full" id="camera-preview" playsinline />
|
<div class="mt-4">
|
||||||
</div>
|
<.camera_button camera={@camera} />
|
||||||
|
<video
|
||||||
|
:if={!(@camera == :denied)}
|
||||||
|
class="hidden h-96 w-full"
|
||||||
|
id="camera-preview"
|
||||||
|
playsinline
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<ul class="mt-5 divide-y divide-zinc-100 dark:divide-slate-300/30">
|
<aside class={[
|
||||||
<li
|
"md:col-span-2",
|
||||||
:for={scan_result <- @scan_results}
|
"border-t md:border-t-0 md:border-l md:border-zinc-200 md:dark:border-zinc-800",
|
||||||
id={scan_result.number}
|
"flex flex-col"
|
||||||
class="flex justify-between gap-x-6 py-5"
|
]}>
|
||||||
phx-mounted={
|
<div class="px-4 py-3 flex items-center justify-between border-b border-zinc-200 dark:border-zinc-800">
|
||||||
JS.transition(
|
<div class="flex items-center gap-2">
|
||||||
{"first:ease-in duration-300", "first:opacity-0 first:p-0 first:h-0",
|
<p class="text-sm font-semibold text-zinc-700 dark:text-zinc-300">
|
||||||
"first:opacity-100"},
|
{gettext("Cart")}
|
||||||
time: 300
|
</p>
|
||||||
)
|
<span class="text-xs text-zinc-500 dark:text-zinc-400">
|
||||||
}
|
{ngettext(
|
||||||
>
|
"%{count} record",
|
||||||
<.scan_result scan_result={scan_result} />
|
"%{count} records",
|
||||||
</li>
|
length(@scan_results),
|
||||||
</ul>
|
count: length(@scan_results)
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
:if={@scan_results != []}
|
||||||
|
type="button"
|
||||||
|
phx-click="clear_results"
|
||||||
|
phx-target={@myself}
|
||||||
|
class="text-xs text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"
|
||||||
|
>
|
||||||
|
{gettext("Clear all")}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
phx-click="toggle_cart"
|
||||||
|
phx-target={@myself}
|
||||||
|
class="rounded-md p-1 text-zinc-500 hover:bg-zinc-200 dark:hover:bg-zinc-800 md:hidden"
|
||||||
|
aria-label={gettext("Toggle cart")}
|
||||||
|
>
|
||||||
|
<.icon
|
||||||
|
name={if @cart_expanded?, do: "hero-chevron-down", else: "hero-chevron-up"}
|
||||||
|
class="size-4"
|
||||||
|
aria-hidden="true"
|
||||||
|
data-slot="icon"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 flex justify-center">
|
<div class={["md:!block", not @cart_expanded? && "hidden"]}>
|
||||||
<.button
|
<div
|
||||||
variant="solid"
|
:if={@scan_results == []}
|
||||||
disabled={length(@scan_results) == 0}
|
id="cart-empty"
|
||||||
phx-disable-with={gettext("Adding...")}
|
class="flex flex-col items-center justify-center gap-2 px-6 py-10 text-center"
|
||||||
phx-click={JS.push("import_releases", target: "#barcode-scanner")}
|
>
|
||||||
>
|
<.barcode_icon class="size-8 text-zinc-400" />
|
||||||
{gettext("Add releases")}
|
<p class="text-sm text-zinc-500 dark:text-zinc-400">
|
||||||
</.button>
|
{gettext("Your cart is empty")}
|
||||||
|
</p>
|
||||||
|
<p class="text-xs text-zinc-500 dark:text-zinc-400">
|
||||||
|
{gettext("Scan barcodes to add records.")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
:if={@scan_results != []}
|
||||||
|
id="cart-items"
|
||||||
|
class="divide-y divide-zinc-200 dark:divide-zinc-800 md:max-h-[calc(100vh-20rem)] overflow-y-auto"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
:for={result <- @scan_results}
|
||||||
|
id={"cart-item-#{result.number}"}
|
||||||
|
class="flex gap-3 px-4 py-3"
|
||||||
|
phx-mounted={
|
||||||
|
JS.transition(
|
||||||
|
{"first:ease-in duration-300", "first:opacity-0 first:p-0 first:h-0",
|
||||||
|
"first:opacity-100"},
|
||||||
|
time: 300
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<.cart_item result={result} myself={@myself} />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div
|
||||||
|
:if={@scan_results != []}
|
||||||
|
class="border-t border-zinc-200 dark:border-zinc-800 px-4 py-3"
|
||||||
|
>
|
||||||
|
<.button
|
||||||
|
variant="solid"
|
||||||
|
phx-disable-with={gettext("Adding...")}
|
||||||
|
phx-click={JS.push("import_releases", target: "#barcode-scanner")}
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
{ngettext(
|
||||||
|
"Add %{count} release",
|
||||||
|
"Add %{count} releases",
|
||||||
|
length(@scan_results),
|
||||||
|
count: length(@scan_results)
|
||||||
|
)}
|
||||||
|
</.button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script :type={Phoenix.LiveView.ColocatedHook} name=".BarcodeScanner">
|
<script :type={Phoenix.LiveView.ColocatedHook} name=".BarcodeScanner">
|
||||||
@@ -75,6 +160,7 @@ defmodule MusicLibraryWeb.Components.BarcodeScanner do
|
|||||||
async mounted() {
|
async mounted() {
|
||||||
const detectedBarcodes = new Set([]);
|
const detectedBarcodes = new Set([]);
|
||||||
const barcodeDetector = await barcodeReaderSetup();
|
const barcodeDetector = await barcodeReaderSetup();
|
||||||
|
this.detectedBarcodes = detectedBarcodes;
|
||||||
this.cameraPreview = this.el.querySelector("#camera-preview");
|
this.cameraPreview = this.el.querySelector("#camera-preview");
|
||||||
const constraints = {
|
const constraints = {
|
||||||
audio: false,
|
audio: false,
|
||||||
@@ -86,6 +172,12 @@ defmodule MusicLibraryWeb.Components.BarcodeScanner do
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
this.handleEvent("remove_barcode", ({ number }) => {
|
||||||
|
detectedBarcodes.delete(number);
|
||||||
|
});
|
||||||
|
this.handleEvent("clear_barcodes", () => {
|
||||||
|
detectedBarcodes.clear();
|
||||||
|
});
|
||||||
this.el.addEventListener("camera_request", () => {
|
this.el.addEventListener("camera_request", () => {
|
||||||
navigator.mediaDevices
|
navigator.mediaDevices
|
||||||
.getUserMedia(constraints)
|
.getUserMedia(constraints)
|
||||||
@@ -318,78 +410,102 @@ defmodule MusicLibraryWeb.Components.BarcodeScanner do
|
|||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
attr :scan_result, BarcodeScan.Result, required: true
|
attr :result, BarcodeScan.Result, required: true
|
||||||
|
attr :myself, :any, required: true
|
||||||
|
|
||||||
defp scan_result(assigns) do
|
defp cart_item(%{result: %{status: :not_found}} = assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<.barcode_not_found :if={@scan_result.status == :not_found} number={@scan_result.number} />
|
<.barcode_icon class="w-12 h-12 flex-none rounded-md p-2 bg-red-50 text-red-400 dark:bg-red-950" />
|
||||||
<.release
|
<div class="min-w-0 flex-1">
|
||||||
:if={@scan_result.status != :not_found}
|
<p class="truncate text-xs text-zinc-500 dark:text-zinc-400">
|
||||||
release={@scan_result.release}
|
{@result.number}
|
||||||
record_id={@scan_result.record_id}
|
</p>
|
||||||
status={@scan_result.status}
|
<p class="truncate text-sm font-medium text-zinc-700 dark:text-zinc-300">
|
||||||
/>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
attr :number, :string, required: true
|
|
||||||
|
|
||||||
defp barcode_not_found(assigns) do
|
|
||||||
~H"""
|
|
||||||
<div class="w-full bg-red-50 p-4 dark:bg-red-950">
|
|
||||||
<h1 class="text-sm/6 text-zinc-700 dark:text-zinc-400">
|
|
||||||
{gettext("Barcode not found")}
|
{gettext("Barcode not found")}
|
||||||
</h1>
|
</p>
|
||||||
<h2 class="mt-1 flex text-sm/5 font-semibold text-wrap text-zinc-700 sm:text-base dark:text-zinc-300">
|
<div class="mt-1 flex items-center gap-2">
|
||||||
{@number}
|
<.status_badge status={:not_found} />
|
||||||
</h2>
|
<button
|
||||||
|
type="button"
|
||||||
|
phx-click="remove_result"
|
||||||
|
phx-value-number={@result.number}
|
||||||
|
phx-target={@myself}
|
||||||
|
class="text-xs text-zinc-500 hover:text-red-600 dark:hover:text-red-400"
|
||||||
|
>
|
||||||
|
{gettext("Remove")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
attr :release, MusicBrainz.ReleaseSearchResult, required: true
|
defp cart_item(assigns) do
|
||||||
attr :record_id, :string
|
|
||||||
attr :status, :atom, required: true, values: [:collected, :wishlisted, :new]
|
|
||||||
|
|
||||||
defp release(assigns) do
|
|
||||||
~H"""
|
~H"""
|
||||||
<div class="flex w-full items-center justify-between">
|
<img
|
||||||
<img
|
class="w-12 h-12 rounded-md flex-none object-cover"
|
||||||
class="mr-4 w-16 flex-none rounded-lg md:w-20"
|
alt={@result.release.release_group.title}
|
||||||
alt={@release.release_group.title}
|
src={ReleaseGroupSearchResult.thumb_url(@result.release.release_group)}
|
||||||
src={ReleaseGroupSearchResult.thumb_url(@release.release_group)}
|
onerror={"this.src = '" <> ~p"/images/cover-not-found.png" <> "';"}
|
||||||
onerror={"this.src = '" <> ~p"/images/cover-not-found.png" <> "';"}
|
/>
|
||||||
/>
|
<div class="min-w-0 flex-1">
|
||||||
<div class="min-w-0 flex-auto">
|
<p class="truncate text-xs text-zinc-500 dark:text-zinc-400">
|
||||||
<h1 class="text-sm/6 text-zinc-700 dark:text-zinc-400">
|
{@result.release.artists}
|
||||||
{@release.artists}
|
</p>
|
||||||
</h1>
|
<p class="truncate text-sm font-medium text-zinc-700 dark:text-zinc-300">
|
||||||
<h2 class="mt-1 flex text-sm/5 font-semibold text-wrap text-zinc-700 sm:text-base dark:text-zinc-300">
|
{@result.release.title}
|
||||||
{@release.title}
|
</p>
|
||||||
</h2>
|
<p class="truncate text-xs/5 text-zinc-500 dark:text-zinc-400">
|
||||||
<p class="mt-1 text-xs/5 text-zinc-500 dark:text-zinc-400">
|
{release_format_label(@result.release)} · {Records.Record.format_release_date(
|
||||||
{release_format_label(@release)} · {Records.Record.format_release_date(@release.date)} · {RecordComponents.type_label(
|
@result.release.date
|
||||||
@release.release_group.type
|
)} · {RecordComponents.type_label(@result.release.release_group.type)}
|
||||||
)}
|
</p>
|
||||||
</p>
|
<div class="mt-1 flex items-center gap-2">
|
||||||
|
<.status_badge status={@result.status} record_id={@result.record_id} />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
phx-click="remove_result"
|
||||||
|
phx-value-number={@result.number}
|
||||||
|
phx-target={@myself}
|
||||||
|
class="text-xs text-zinc-500 hover:text-red-600 dark:hover:text-red-400"
|
||||||
|
>
|
||||||
|
{gettext("Remove")}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<.badge :if={@status == :new}>
|
|
||||||
{gettext("New")}
|
|
||||||
</.badge>
|
|
||||||
<.link :if={@status == :wishlisted} navigate={~p"/wishlist/#{@record_id}"}>
|
|
||||||
<.badge color="warning">
|
|
||||||
{gettext("Wishlisted")}
|
|
||||||
</.badge>
|
|
||||||
</.link>
|
|
||||||
<.link :if={@status == :collected} navigate={~p"/collection/#{@record_id}"}>
|
|
||||||
<.badge color="success">
|
|
||||||
{gettext("Collected")}
|
|
||||||
</.badge>
|
|
||||||
</.link>
|
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr :status, :atom, required: true, values: [:new, :wishlisted, :collected, :not_found]
|
||||||
|
attr :record_id, :string, default: nil
|
||||||
|
|
||||||
|
defp status_badge(%{status: :new} = assigns) do
|
||||||
|
~H"""
|
||||||
|
<.badge>{gettext("New")}</.badge>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp status_badge(%{status: :wishlisted} = assigns) do
|
||||||
|
~H"""
|
||||||
|
<.link navigate={~p"/wishlist/#{@record_id}"}>
|
||||||
|
<.badge color="warning">{gettext("Wishlisted")}</.badge>
|
||||||
|
</.link>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp status_badge(%{status: :collected} = assigns) do
|
||||||
|
~H"""
|
||||||
|
<.link navigate={~p"/collection/#{@record_id}"}>
|
||||||
|
<.badge color="success">{gettext("Collected")}</.badge>
|
||||||
|
</.link>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp status_badge(%{status: :not_found} = assigns) do
|
||||||
|
~H"""
|
||||||
|
<.badge color="danger">{gettext("Not found")}</.badge>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("camera_allowed", _params, socket) do
|
def handle_event("camera_allowed", _params, socket) do
|
||||||
{:noreply, assign(socket, camera: :allowed)}
|
{:noreply, assign(socket, camera: :allowed)}
|
||||||
@@ -420,6 +536,26 @@ defmodule MusicLibraryWeb.Components.BarcodeScanner do
|
|||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("remove_result", %{"number" => number}, socket) do
|
||||||
|
scan_results = Enum.reject(socket.assigns.scan_results, &(&1.number == number))
|
||||||
|
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> assign(:scan_results, scan_results)
|
||||||
|
|> push_event("remove_barcode", %{number: number})}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("clear_results", _params, socket) do
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> assign(:scan_results, [])
|
||||||
|
|> push_event("clear_barcodes", %{})}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("toggle_cart", _params, socket) do
|
||||||
|
{:noreply, assign(socket, :cart_expanded?, not socket.assigns.cart_expanded?)}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_event("import_releases", _params, socket) do
|
def handle_event("import_releases", _params, socket) do
|
||||||
current_time = DateTime.utc_now()
|
current_time = DateTime.utc_now()
|
||||||
scan_results = socket.assigns.scan_results
|
scan_results = socket.assigns.scan_results
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ defmodule MusicLibraryWeb.CollectionLive.Index do
|
|||||||
:if={@live_action == :barcode_scan}
|
:if={@live_action == :barcode_scan}
|
||||||
id="barcode-scanner-modal"
|
id="barcode-scanner-modal"
|
||||||
on_close={JS.patch(back_path(@record_list_params))}
|
on_close={JS.patch(back_path(@record_list_params))}
|
||||||
|
width_class="md:max-w-4xl lg:max-w-5xl"
|
||||||
>
|
>
|
||||||
<.live_component
|
<.live_component
|
||||||
module={MusicLibraryWeb.Components.BarcodeScanner}
|
module={MusicLibraryWeb.Components.BarcodeScanner}
|
||||||
|
|||||||
@@ -536,11 +536,6 @@ msgstr ""
|
|||||||
msgid "Add new Record · Wishlist"
|
msgid "Add new Record · Wishlist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/barcode_scanner.ex
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Add releases"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/music_library_web/components/barcode_scanner.ex
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Adding..."
|
msgid "Adding..."
|
||||||
@@ -1588,6 +1583,7 @@ msgid "Record set updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#: lib/music_library_web/live/record_set_live/index.ex
|
#: lib/music_library_web/live/record_set_live/index.ex
|
||||||
#: lib/music_library_web/live/record_set_live/show.ex
|
#: lib/music_library_web/live/record_set_live/show.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -2464,6 +2460,7 @@ msgid "A rule for this album already exists"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{count} record"
|
msgid "%{count} record"
|
||||||
msgid_plural "%{count} records"
|
msgid_plural "%{count} records"
|
||||||
@@ -2476,6 +2473,7 @@ msgid "Add records from the search results to get started."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Cart"
|
msgid "Cart"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2486,6 +2484,7 @@ msgid "Choose which format to add"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Clear all"
|
msgid "Clear all"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2498,11 +2497,13 @@ msgstr[0] ""
|
|||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Toggle cart"
|
msgid "Toggle cart"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Your cart is empty"
|
msgid "Your cart is empty"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2516,3 +2517,20 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "In cart"
|
msgid "In cart"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Add %{count} release"
|
||||||
|
msgid_plural "Add %{count} releases"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Scan barcodes to add records."
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -536,11 +536,6 @@ msgstr ""
|
|||||||
msgid "Add new Record · Wishlist"
|
msgid "Add new Record · Wishlist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/barcode_scanner.ex
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Add releases"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/music_library_web/components/barcode_scanner.ex
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Adding..."
|
msgid "Adding..."
|
||||||
@@ -1588,6 +1583,7 @@ msgid "Record set updated successfully"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#: lib/music_library_web/live/record_set_live/index.ex
|
#: lib/music_library_web/live/record_set_live/index.ex
|
||||||
#: lib/music_library_web/live/record_set_live/show.ex
|
#: lib/music_library_web/live/record_set_live/show.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -2464,6 +2460,7 @@ msgid "A rule for this album already exists"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{count} record"
|
msgid "%{count} record"
|
||||||
msgid_plural "%{count} records"
|
msgid_plural "%{count} records"
|
||||||
@@ -2476,6 +2473,7 @@ msgid "Add records from the search results to get started."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Cart"
|
msgid "Cart"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2486,6 +2484,7 @@ msgid "Choose which format to add"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Clear all"
|
msgid "Clear all"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2498,11 +2497,13 @@ msgstr[0] ""
|
|||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Toggle cart"
|
msgid "Toggle cart"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/add_record.ex
|
#: lib/music_library_web/components/add_record.ex
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Your cart is empty"
|
msgid "Your cart is empty"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2516,3 +2517,20 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "In cart"
|
msgid "In cart"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Add %{count} release"
|
||||||
|
msgid_plural "Add %{count} releases"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Scan barcodes to add records."
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -585,9 +585,9 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do
|
|||||||
conn
|
conn
|
||||||
|> visit(~p"/collection/scan")
|
|> visit(~p"/collection/scan")
|
||||||
|> trigger_hook("#barcode-scanner", "barcode_scanned", %{"number" => barcode})
|
|> trigger_hook("#barcode-scanner", "barcode_scanned", %{"number" => barcode})
|
||||||
|> assert_has("h2", "Marbles")
|
|> assert_has("#cart-items", text: "Marbles")
|
||||||
|> assert_has("span", "New")
|
|> assert_has("span", "New")
|
||||||
|> click_button("Add releases")
|
|> click_button("Add 1 release")
|
||||||
|
|
||||||
[record] = MusicLibrary.Repo.all(MusicLibrary.Records.Record)
|
[record] = MusicLibrary.Repo.all(MusicLibrary.Records.Record)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user