mirror of
https://github.com/sbondCo/Watcharr.git
synced 2026-08-07 07:14:44 +00:00
import: Add rating column (validated to be 0-10) and set import item state to FAILED if request fails
This commit is contained in:
@@ -7,6 +7,7 @@ These changes are awaiting release:
|
|||||||
- HTTP Requests: Replace `axios` with `Fetch`.
|
- HTTP Requests: Replace `axios` with `Fetch`.
|
||||||
- Show `Airs On` date for episodes that have yet to air (thanks [@KarpachMarko]!).
|
- Show `Airs On` date for episodes that have yet to air (thanks [@KarpachMarko]!).
|
||||||
- Show `Aired Date` for episodes.
|
- Show `Aired Date` for episodes.
|
||||||
|
- Import: Add a `rating` column.
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
@@ -14,6 +15,9 @@ These changes are awaiting release:
|
|||||||
- Removed some legacy code that still thought there was a `store.watchedList`.
|
- Removed some legacy code that still thought there was a `store.watchedList`.
|
||||||
- DropDown: Fix whole page scrolling when pressing letter to scroll to first relevant dropdown item (only the dropdown list should scroll now, which is less jarring).
|
- DropDown: Fix whole page scrolling when pressing letter to scroll to first relevant dropdown item (only the dropdown list should scroll now, which is less jarring).
|
||||||
- Media pages: Only show PosterImage if we have a poster path.
|
- Media pages: Only show PosterImage if we have a poster path.
|
||||||
|
- Import:
|
||||||
|
- Mark import item as failed correctly when error is thrown from doImport.
|
||||||
|
- Make the table horizontally scrollable when necessary (instead of just cutting off the end).
|
||||||
|
|
||||||
## Maintenance
|
## Maintenance
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
let isImporting = $state(false);
|
let isImporting = $state(false);
|
||||||
let importText = $state("");
|
let importText = $state("");
|
||||||
let cancelled = $state(false);
|
let cancelled = $state(false);
|
||||||
|
let importTableEl: HTMLTableElement | undefined = $state();
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
@@ -432,8 +433,47 @@
|
|||||||
rList = rList;
|
rList = rList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inputs that have a `data-validateme` property, we will validate with the
|
||||||
|
* browser.
|
||||||
|
*/
|
||||||
|
function validatableInputsAreValid(): boolean {
|
||||||
|
if (!importTableEl) {
|
||||||
|
// Somehow the table isn't defined, i guess allow continuing so we
|
||||||
|
// dont block.
|
||||||
|
console.warn("validatableInputsAreValid: There is no table element.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const inputs = importTableEl.querySelectorAll<HTMLInputElement>(
|
||||||
|
"tbody input[data-validateme]",
|
||||||
|
);
|
||||||
|
if (inputs.length <= 0) {
|
||||||
|
console.warn("validatableInputsAreValid: No inputs found.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (const input of inputs) {
|
||||||
|
if (!input.reportValidity()) {
|
||||||
|
// Stop the for loop after we hit one input that is invalid.
|
||||||
|
// No need to continue after we know at least one is invalid.
|
||||||
|
console.error("validatableInputsAreValid: Invalid input found.", input);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
async function startImport() {
|
async function startImport() {
|
||||||
console.log(rList);
|
if (!validatableInputsAreValid()) {
|
||||||
|
console.warn(
|
||||||
|
"startImport: Some fields are not valid, not starting import!",
|
||||||
|
);
|
||||||
|
notify({
|
||||||
|
type: "error",
|
||||||
|
text: "An error was found in one of the inputs, please fix it and try starting the import again.",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("startImport: Starting.", rList);
|
||||||
isImporting = true;
|
isImporting = true;
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
for (let i = 0; i < rList.length; i++) {
|
for (let i = 0; i < rList.length; i++) {
|
||||||
@@ -446,6 +486,7 @@
|
|||||||
console.log("Importing", li);
|
console.log("Importing", li);
|
||||||
await doImport(li);
|
await doImport(li);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
li.state = ImportResponseType.IMPORT_FAILED;
|
||||||
console.error("Failed to import item:", li, "reason:", err);
|
console.error("Failed to import item:", li, "reason:", err);
|
||||||
notify({
|
notify({
|
||||||
type: "error",
|
type: "error",
|
||||||
@@ -596,131 +637,150 @@
|
|||||||
You can fix any failed imports when the process completes.
|
You can fix any failed imports when the process completes.
|
||||||
{/if}
|
{/if}
|
||||||
</h5>
|
</h5>
|
||||||
<table class={isImporting ? "is-importing" : ""}>
|
<div class="table-wrap">
|
||||||
<thead>
|
<table
|
||||||
<tr>
|
bind:this={importTableEl}
|
||||||
{#if isImporting}
|
class={isImporting ? "is-importing" : ""}
|
||||||
<th class="loading-col"></th>
|
>
|
||||||
{/if}
|
<thead>
|
||||||
<th>Name</th>
|
|
||||||
<th>Year</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Status</th>
|
|
||||||
{#if !isImporting}
|
|
||||||
<th></th>
|
|
||||||
{/if}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<!-- TODO: Fix this to use a keyed each somehow (need unique id for key) -->
|
|
||||||
<!-- eslint-disable-next-line svelte/require-each-key -->
|
|
||||||
{#each rList as l}
|
|
||||||
<tr>
|
<tr>
|
||||||
{#if isImporting}
|
{#if isImporting}
|
||||||
<td class="icon-cell">
|
<th class="loading-col"></th>
|
||||||
<div>
|
|
||||||
{#if !l.state}
|
|
||||||
<SpinnerTiny />
|
|
||||||
{:else if l.state === ImportResponseType.IMPORT_SUCCESS}
|
|
||||||
<Icon i="check" wh={22} />
|
|
||||||
{:else if l.state === ImportResponseType.IMPORT_NOTFOUND}
|
|
||||||
<Icon i="close" wh={22} />
|
|
||||||
{:else if l.state === ImportResponseType.IMPORT_FAILED}
|
|
||||||
<Icon i="close" wh={22} />
|
|
||||||
{:else if l.state === ImportResponseType.IMPORT_EXISTS}
|
|
||||||
<Icon i="check" wh={22} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
{/if}
|
{/if}
|
||||||
<td>
|
<th>Name</th>
|
||||||
<input
|
<th>Year</th>
|
||||||
class="plain"
|
<th>Type</th>
|
||||||
bind:value={l.name}
|
<th>Status</th>
|
||||||
disabled={isImporting}
|
<th>Rating</th>
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="year">
|
|
||||||
<input
|
|
||||||
class="plain"
|
|
||||||
bind:value={l.year}
|
|
||||||
placeholder="YYYY"
|
|
||||||
type="number"
|
|
||||||
disabled={isImporting}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="type">
|
|
||||||
<DropDown
|
|
||||||
options={dropDownSupportedTypes}
|
|
||||||
bind:active={l.type}
|
|
||||||
placeholder="Type"
|
|
||||||
blendIn={true}
|
|
||||||
disabled={isImporting}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="type">
|
|
||||||
<DropDown
|
|
||||||
options={[
|
|
||||||
"FINISHED",
|
|
||||||
"PLANNED",
|
|
||||||
"WATCHING",
|
|
||||||
"HOLD",
|
|
||||||
"DROPPED",
|
|
||||||
]}
|
|
||||||
bind:active={l.status}
|
|
||||||
placeholder="Status"
|
|
||||||
blendIn={true}
|
|
||||||
disabled={isImporting}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
{#if !isImporting}
|
{#if !isImporting}
|
||||||
<td>
|
<th></th>
|
||||||
<button
|
|
||||||
class="plain delete"
|
|
||||||
onclick={() => {
|
|
||||||
removeRow(l);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon i="close" wh="25" />
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
{/if}
|
{/if}
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
</thead>
|
||||||
{#if !isImporting}
|
<tbody>
|
||||||
<tr>
|
<!-- TODO: Fix this to use a keyed each somehow (need unique id for key) -->
|
||||||
<td
|
<!-- eslint-disable-next-line svelte/require-each-key -->
|
||||||
><input
|
{#each rList as l}
|
||||||
class="plain"
|
<tr>
|
||||||
placeholder="Name"
|
{#if isImporting}
|
||||||
onblur={addRow}
|
<td class="icon-cell">
|
||||||
/></td
|
<div>
|
||||||
>
|
{#if !l.state}
|
||||||
<td class="year">
|
<SpinnerTiny />
|
||||||
<input
|
{:else if l.state === ImportResponseType.IMPORT_SUCCESS}
|
||||||
class="plain"
|
<Icon i="check" wh={22} />
|
||||||
id="addYear"
|
{:else if l.state === ImportResponseType.IMPORT_NOTFOUND}
|
||||||
placeholder="YYYY"
|
<Icon i="close" wh={22} />
|
||||||
type="number"
|
{:else if l.state === ImportResponseType.IMPORT_FAILED}
|
||||||
/>
|
<Icon i="close" wh={22} />
|
||||||
</td>
|
{:else if l.state === ImportResponseType.IMPORT_EXISTS}
|
||||||
<td class="type"></td>
|
<Icon i="check" wh={22} />
|
||||||
<td class="status"></td>
|
{/if}
|
||||||
<td></td>
|
</div>
|
||||||
</tr>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
</tbody>
|
<td class="name">
|
||||||
</table>
|
<input
|
||||||
|
class="plain"
|
||||||
|
bind:value={l.name}
|
||||||
|
disabled={isImporting}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="year">
|
||||||
|
<input
|
||||||
|
class="plain"
|
||||||
|
bind:value={l.year}
|
||||||
|
placeholder="YYYY"
|
||||||
|
type="number"
|
||||||
|
disabled={isImporting}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="type">
|
||||||
|
<DropDown
|
||||||
|
options={dropDownSupportedTypes}
|
||||||
|
bind:active={l.type}
|
||||||
|
placeholder="Type"
|
||||||
|
blendIn={true}
|
||||||
|
disabled={isImporting}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="status">
|
||||||
|
<DropDown
|
||||||
|
options={[
|
||||||
|
"FINISHED",
|
||||||
|
"PLANNED",
|
||||||
|
"WATCHING",
|
||||||
|
"HOLD",
|
||||||
|
"DROPPED",
|
||||||
|
]}
|
||||||
|
bind:active={l.status}
|
||||||
|
placeholder="Status"
|
||||||
|
blendIn={true}
|
||||||
|
disabled={isImporting}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="rating">
|
||||||
|
<input
|
||||||
|
class="plain"
|
||||||
|
data-validateme
|
||||||
|
bind:value={l.rating}
|
||||||
|
placeholder="0"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="10"
|
||||||
|
disabled={isImporting}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
{#if !isImporting}
|
||||||
|
<td>
|
||||||
|
<button
|
||||||
|
class="plain delete"
|
||||||
|
onclick={() => {
|
||||||
|
removeRow(l);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon i="close" wh="25" />
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{/if}
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
{#if !isImporting}
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
><input
|
||||||
|
class="plain"
|
||||||
|
placeholder="Name"
|
||||||
|
onblur={addRow}
|
||||||
|
/></td
|
||||||
|
>
|
||||||
|
<td class="year">
|
||||||
|
<input
|
||||||
|
class="plain"
|
||||||
|
id="addYear"
|
||||||
|
placeholder="YYYY"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="type"></td>
|
||||||
|
<td class="status"></td>
|
||||||
|
<td class="rating"></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
<div class="btns">
|
<div class="btns">
|
||||||
<button onclick={() => goto(resolve("/import"))}
|
<button onclick={() => goto(resolve("/import"))}>
|
||||||
><Icon i="arrow" />Back</button
|
<Icon i="arrow" />Back
|
||||||
>
|
</button>
|
||||||
<button onclick={() => changeAllStatuses()} disabled={isImporting}>
|
<button onclick={() => changeAllStatuses()} disabled={isImporting}>
|
||||||
Change Statuses
|
Change All Statuses
|
||||||
|
</button>
|
||||||
|
<button onclick={startImport} disabled={isImporting}>
|
||||||
|
Start Importing
|
||||||
</button>
|
</button>
|
||||||
<button onclick={startImport} disabled={isImporting}
|
|
||||||
>Start Importing</button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
{#if typeof changeAllStatusesModalCb === "function"}
|
{#if typeof changeAllStatusesModalCb === "function"}
|
||||||
<Modal
|
<Modal
|
||||||
@@ -817,8 +877,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
min-width: 400px;
|
min-width: 400px;
|
||||||
max-width: 600px;
|
max-width: 1200px;
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
@media screen and (max-width: 410px) {
|
@media screen and (max-width: 410px) {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
@@ -826,15 +885,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-wrap {
|
||||||
|
overflow: auto;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
td {
|
td {
|
||||||
|
&.name {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
&.year {
|
&.year {
|
||||||
width: 70px;
|
width: 70px;
|
||||||
|
min-width: 67px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.type {
|
&.type {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.rating {
|
||||||
|
width: 82px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,8 @@
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
table {
|
table {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
td {
|
td {
|
||||||
padding: 12px 15px;
|
padding: 12px 15px;
|
||||||
word-wrap: anywhere;
|
word-wrap: anywhere;
|
||||||
|
|||||||
@@ -263,7 +263,6 @@
|
|||||||
|
|
||||||
:global {
|
:global {
|
||||||
table {
|
table {
|
||||||
margin-top: 20px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-spacing: 0px;
|
border-spacing: 0px;
|
||||||
border: 1px solid $accent-color;
|
border: 1px solid $accent-color;
|
||||||
|
|||||||
Reference in New Issue
Block a user