various styles
This commit is contained in:
1 parent
2761456009
commit
77428aa7de
11 files changed
+111
-52
No files matched your search
@@ -1,14 +1,7 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
info page
|
|
||||||
minimum quality
|
|
||||||
|
|
||||||
acp page needs to fetch offers list
|
acp page needs to fetch offers list
|
||||||
|
|
||||||
on logged in need to redirect to offers
|
|
||||||
|
|
||||||
discord!
|
|
||||||
|
|
||||||
want added -> wants added
|
want added -> wants added
|
||||||
spamming discogs and generating offers on every want add
|
spamming discogs and generating offers on every want add
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ async function start() {
|
|||||||
events,
|
events,
|
||||||
} = createServices(log);
|
} = createServices(log);
|
||||||
|
|
||||||
// events.emit(Events.StartupSync);
|
events.emit(Events.StartupSync);
|
||||||
// events.emit(Events.ListingsUpdated);
|
// events.emit(Events.ListingsUpdated);
|
||||||
|
|
||||||
api.listen();
|
api.listen();
|
||||||
|
|||||||
+2
-2
@@ -72,7 +72,7 @@ export default function createApi(log: Log, pg: Pg, discogs, offers, events) {
|
|||||||
const user = await pg.userFindId(ctx.state.user.id);
|
const user = await pg.userFindId(ctx.state.user.id);
|
||||||
const release = await discogs.release(releaseId);
|
const release = await discogs.release(releaseId);
|
||||||
const want = await pg.wantCreate(release, user);
|
const want = await pg.wantCreate(release, user);
|
||||||
events.emit(Events.WantAdded, want);
|
events.emit(Events.WantsAdded, [want]);
|
||||||
ctx.response.body = [want];
|
ctx.response.body = [want];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ export default function createApi(log: Log, pg: Pg, discogs, offers, events) {
|
|||||||
const { username } = ctx.request.body;
|
const { username } = ctx.request.body;
|
||||||
const user = await pg.userFindId(ctx.state.user.id);
|
const user = await pg.userFindId(ctx.state.user.id);
|
||||||
const wants = await discogs.wantListSync(username, user);
|
const wants = await discogs.wantListSync(username, user);
|
||||||
wants.forEach(w => events.emit(Events.WantAdded, w));
|
events.emit(Events.WantsAdded, [wants]);
|
||||||
ctx.response.body = wants;
|
ctx.response.body = wants;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -84,11 +84,9 @@ export default function createDiscogs(log: Log, pg: Pg) {
|
|||||||
.get();
|
.get();
|
||||||
|
|
||||||
const unlisted = await pg.listingRmUnlisted(want, ids);
|
const unlisted = await pg.listingRmUnlisted(want, ids);
|
||||||
if (unlisted) log.info({ wantId: want.id, unlisted: unlisted }, 'missing listings removed');
|
if (unlisted) log.info({ unlisted: unlisted, ...wantDetails(want) }, 'missing listings removed');
|
||||||
|
|
||||||
// preferentially take listings from sellers if we hit req limits
|
// preferentially take listings from sellers if we hit req limits
|
||||||
|
|
||||||
log.info({ listings: ids.length, ...wantDetails(want)}, 'found listings');
|
|
||||||
return Bluebird.mapSeries(ids, async id => listing(id, want));
|
return Bluebird.mapSeries(ids, async id => listing(id, want));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-12
@@ -9,7 +9,7 @@ import { wantDetails } from './utils';
|
|||||||
import routerAuth from './routers/router.auth';
|
import routerAuth from './routers/router.auth';
|
||||||
|
|
||||||
export enum Events {
|
export enum Events {
|
||||||
WantAdded = 'WantAdded',
|
WantsAdded = 'WantsAdded',
|
||||||
StartupSync = 'StartupSync',
|
StartupSync = 'StartupSync',
|
||||||
ListingsUpdated = 'ListingsUpdated',
|
ListingsUpdated = 'ListingsUpdated',
|
||||||
Connection = 'Connection',
|
Connection = 'Connection',
|
||||||
@@ -37,26 +37,28 @@ export default function createEvents(log: Log, pg: Pg, discogs, offers) {
|
|||||||
log.error('event error', e);
|
log.error('event error', e);
|
||||||
});
|
});
|
||||||
|
|
||||||
events.on(Events.WantAdded, async function wantAdded(want: Want) {
|
async function updateWantListings(wants: Want[]) {
|
||||||
log.info({ event: Events.WantAdded, ...wantDetails(want) });
|
|
||||||
const listings = await populateListings(want);
|
|
||||||
log.info({ listings: listings.length, ...wantDetails(want) }, 'listings added');
|
|
||||||
events.emit(Events.ListingsUpdated);
|
|
||||||
});
|
|
||||||
|
|
||||||
events.on(Events.StartupSync, async function wantAdded() {
|
|
||||||
const wants = await pg.wantsToSync();
|
|
||||||
log.info({ event: Events.StartupSync, wants: wants.length });
|
|
||||||
|
|
||||||
for (const want of wants) {
|
for (const want of wants) {
|
||||||
const listings = await populateListings(want);
|
const listings = await populateListings(want);
|
||||||
log.info({ listings: listings.length, ...wantDetails(want) }, 'listings added');
|
log.info({ listings: listings.length, ...wantDetails(want) }, 'listings added');
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info({ wants: wants.length }, 'finished syncing');
|
log.info({ wants: wants.length }, 'finished syncing');
|
||||||
|
}
|
||||||
|
|
||||||
|
events.on(Events.WantsAdded, async function wantsAdded(wants: Want[]) {
|
||||||
|
log.info({ event: Events.WantsAdded, wants: wants.map(w => wantDetails(w)) }, 'starting listings population');
|
||||||
|
await updateWantListings(wants);
|
||||||
|
log.info({ event: Events.WantsAdded, wants: wants.map(w => wantDetails(w)) }, 'finished populating listings');
|
||||||
events.emit(Events.ListingsUpdated);
|
events.emit(Events.ListingsUpdated);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
events.on(Events.StartupSync, async function startupSync() {
|
||||||
|
const wants = await pg.wantsToSync();
|
||||||
|
log.info({ event: Events.StartupSync, wants: wants.length });
|
||||||
|
await updateWantListings(wants);
|
||||||
|
events.emit(Events.ListingsUpdated);
|
||||||
|
});
|
||||||
|
|
||||||
events.on(Events.ListingsUpdated, async function listingsUpdated() {
|
events.on(Events.ListingsUpdated, async function listingsUpdated() {
|
||||||
const bundles = await offers.bundles();
|
const bundles = await offers.bundles();
|
||||||
|
|||||||
+9
-5
@@ -9,11 +9,15 @@ import { logAxiosError, wantDetails } from './utils';
|
|||||||
import { Pg } from './pg';
|
import { Pg } from './pg';
|
||||||
|
|
||||||
export enum Status {
|
export enum Status {
|
||||||
Pending = 'Pending',
|
|
||||||
Accepted = 'Accepted',
|
|
||||||
Declined = 'Declined',
|
|
||||||
Confirmed = 'Confirmed',
|
|
||||||
Complete = 'Complete',
|
Complete = 'Complete',
|
||||||
|
Confirmed = 'Confirmed',
|
||||||
|
Accepted = 'Accepted',
|
||||||
|
Pending = 'Pending',
|
||||||
|
Declined = 'Declined',
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortOffers(o) {
|
||||||
|
return Object.values(Status).indexOf(o.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function createOffers(log: Log, pg: Pg) {
|
export default function createOffers(log: Log, pg: Pg) {
|
||||||
@@ -130,7 +134,7 @@ export default function createOffers(log: Log, pg: Pg) {
|
|||||||
seller: offers[0]?.listing.data.seller.id,
|
seller: offers[0]?.listing.data.seller.id,
|
||||||
shipsFrom: offers[0]?.listing.data.ships_from,
|
shipsFrom: offers[0]?.listing.data.ships_from,
|
||||||
shipping,
|
shipping,
|
||||||
offers,
|
offers: sortBy(offers, sortOffers),
|
||||||
mentionsAustralia: shipping.toLowerCase().includes('australia'),
|
mentionsAustralia: shipping.toLowerCase().includes('australia'),
|
||||||
mentionsWorldwide: shipping.toLowerCase().includes('world'),
|
mentionsWorldwide: shipping.toLowerCase().includes('world'),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ export default function Offer(offer) {
|
|||||||
return (
|
return (
|
||||||
<div class="offer">
|
<div class="offer">
|
||||||
<section>
|
<section>
|
||||||
<h2>{details.artists_sort} - {details.title} <a href={details.uri} target="_blank"><ExternalLink /></a></h2>
|
<h3>{details.artists_sort} - {details.title} <a href={details.uri} target="_blank"><ExternalLink /></a></h3>
|
||||||
<h5 class='alt-text' >{details.year}</h5>
|
<div class='alt-text' >{details.year}</div>
|
||||||
<h5 class='alt-text'>{details.formats[0].descriptions.join(' ')}</h5>
|
<div class='alt-text'>{details.formats[0].descriptions.join(' ')}</div>
|
||||||
<h5 class='number'>mcrates price: ${offer.total}</h5>
|
<div class='number'>mcrates price: ${offer.total}</div>
|
||||||
<h5 class='number'>shipped by itself: ${offer.comparison}</h5>
|
<div class='number'>shipped by itself: ${offer.comparison}</div>
|
||||||
</section>
|
</section>
|
||||||
<img src={details.thumb} />
|
<img src={details.thumb} />
|
||||||
<button onClick={() => setStatus('Accepted')} disabled={accepted} class={classes} >{btnText}</button>
|
<button onClick={() => setStatus('Accepted')} disabled={accepted} class={classes} >{btnText}</button>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import { useState, useCallback } from 'preact/hooks';
|
import { useState, useCallback } from 'preact/hooks';
|
||||||
|
import { ExternalLink } from 'react-feather';
|
||||||
|
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { State } from './../store'
|
import { State } from './../store'
|
||||||
@@ -25,11 +26,13 @@ function Want(want) {
|
|||||||
return (
|
return (
|
||||||
<article key={r.id}>
|
<article key={r.id}>
|
||||||
<div>
|
<div>
|
||||||
<h3>{r.artists_sort} - {r.title}</h3>
|
<h3>{r.artists_sort} - {r.title} <a href={r.uri} target="_blank"><ExternalLink /></a></h3>
|
||||||
<h5 class='alt-text' >{r.year}</h5>
|
|
||||||
<h5 class='alt-text'>{r.formats[0].descriptions.join(' ')}</h5>
|
|
||||||
</div>
|
</div>
|
||||||
<img src={r.thumb} />
|
<img src={r.thumb} />
|
||||||
|
<div class='details'>
|
||||||
|
<div class='alt'>{r.year}</div>
|
||||||
|
<div class='alt'>{r.formats[0].descriptions.join(' ')}</div>
|
||||||
|
</div>
|
||||||
<button onClick={onClick} >{text}</button>
|
<button onClick={onClick} >{text}</button>
|
||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ import { useSelector, useDispatch } from 'react-redux'
|
|||||||
import { State } from './../store';
|
import { State } from './../store';
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
Pending = 'Pending',
|
|
||||||
Accepted = 'Accepted',
|
Accepted = 'Accepted',
|
||||||
|
Pending = 'Pending',
|
||||||
Declined = 'Declined',
|
Declined = 'Declined',
|
||||||
Ordered = 'Ordered',
|
Confirmed = 'Confirmed',
|
||||||
Complete = 'Complete',
|
Complete = 'Complete',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const POSITIVE_STATUS = [Status.Accepted, Status.Confirmed, Status.Complete];
|
||||||
|
|
||||||
function Item({ item }) {
|
function Item({ item }) {
|
||||||
const ws = useSelector((state: State) => state.ws);
|
const ws = useSelector((state: State) => state.ws);
|
||||||
console.log(item);
|
console.log(item);
|
||||||
@@ -26,8 +28,12 @@ function Item({ item }) {
|
|||||||
ws.sendAcpOfferStatusSet(item.id, e.target.value);
|
ws.sendAcpOfferStatusSet(item.id, e.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bold = POSITIVE_STATUS.includes(item.status)
|
||||||
|
? 'bold'
|
||||||
|
: '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={item.id}>
|
<div key={item.id} class={bold}>
|
||||||
<a href={item.listing.data.uri} target="_blank" >
|
<a href={item.listing.data.uri} target="_blank" >
|
||||||
<h4>{item.listing.data.release.description}</h4>
|
<h4>{item.listing.data.release.description}</h4>
|
||||||
</a>
|
</a>
|
||||||
@@ -43,11 +49,13 @@ function Item({ item }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SellerGroup(group) {
|
function SellerGroup(group) {
|
||||||
|
const ready = group.offers.filter(item => POSITIVE_STATUS.includes(item.status)).length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="bundle">
|
<div class="bundle">
|
||||||
<section>
|
<section>
|
||||||
<h2>{group.username} - {group.seller}</h2>
|
<h2>{group.username} - {group.seller}</h2>
|
||||||
<h3>{group.offers.length} offers</h3>
|
<h3>{ready} / {group.offers.length} offers</h3>
|
||||||
<h4>Ships: {group.mentionsAustralia && 'AU'} {group.mentionsWorldwide && 'WORLD'}</h4>
|
<h4>Ships: {group.mentionsAustralia && 'AU'} {group.mentionsWorldwide && 'WORLD'}</h4>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,15 @@ const Info = () => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h3>How It Works</h3>
|
<h2>How It Works</h2>
|
||||||
<p> First, add some records to your <span class='alt'>List</span> on the <a href='/search'>Search Page</a>. </p>
|
<p> First, add some records to your <span class='alt'>List</span> on the <a href='/search'>Search Page</a>. </p>
|
||||||
<p> Mates Crates will then make offers to you for releases you add to your <a href='/list'>List</a>. </p>
|
<p> Mates Crates will then make <span class='alt'>Offers</span> to you for releases you add to your <a href='/list'>List</a>. </p>
|
||||||
|
<p> Check back regularly for updated <span class='alt'>Offers</span>, the global record marketplace is a living thing. </p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Offers</h2>
|
<h2>Offers</h2>
|
||||||
|
<p> Not all items on your <span class='alt'>List</span> will have offers. However, as new users join the site the number of offers we can make increases, so be sure to tell your friends!</p>
|
||||||
<p> Think of offers like an expression of interest, they are not confirmed until they are set to <span class='alt-text'>Confirmed</span> status. </p>
|
<p> Think of offers like an expression of interest, they are not confirmed until they are set to <span class='alt-text'>Confirmed</span> status. </p>
|
||||||
<p> After that they will be shipped to Melbourne and ready for pick up. </p>
|
<p> After that they will be shipped to Melbourne and ready for pick up. </p>
|
||||||
</section>
|
</section>
|
||||||
@@ -32,9 +34,11 @@ const Info = () => {
|
|||||||
<section>
|
<section>
|
||||||
<h2>FAQ</h2>
|
<h2>FAQ</h2>
|
||||||
<h3> Record Condition </h3>
|
<h3> Record Condition </h3>
|
||||||
<p> All Mates Crates offers are for records that are either mint condition or close to it. </p>
|
<p> All Mates Crates offers are for records that are either mint or very good condition. </p>
|
||||||
<h3> Delivery Times </h3>
|
<h3> Delivery Times </h3>
|
||||||
<p> Records are usually available for pickup within a week. We order in bulk, which often means express, insured shipping to Melbourne. </p>
|
<p> Records are usually available for pickup within a week. We order in bulk, which often means express, insured shipping to Melbourne. </p>
|
||||||
|
<h3> Duplicates </h3>
|
||||||
|
<p> Sometimes we can get a record from multiple sources, these appear as duplicate offers. Accepting multiple offers increases the chance the item will be confirmed. </p>
|
||||||
<h3> Friends </h3>
|
<h3> Friends </h3>
|
||||||
<p> Please invite other collectors to the site. More users means offers can become cheaper and shipped sooner. </p>
|
<p> Please invite other collectors to the site. More users means offers can become cheaper and shipped sooner. </p>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ html {
|
|||||||
box-sizing: inherit;
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
body, h1, h2, h3, h4, h5, h6, p, ol, ul {
|
body, h4, h5, h6, p, ol, ul {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
@@ -100,9 +100,9 @@ html {
|
|||||||
--max-font-size: var(--large-font-size);
|
--max-font-size: var(--large-font-size);
|
||||||
|
|
||||||
--base-border-color: var(--secondary-font-color);
|
--base-border-color: var(--secondary-font-color);
|
||||||
--base-border: 1px solid var(--base-border-color);
|
--base-border: 2px solid var(--base-border-color);
|
||||||
--hover-border: 1px solid var(--base-font-color);
|
--hover-border: 2px solid var(--base-font-color);
|
||||||
--focus-border: 1px solid var(--base-font-color);
|
--focus-border: 2px solid var(--base-font-color);
|
||||||
--base-border-radius: .2rem;
|
--base-border-radius: .2rem;
|
||||||
--base-focus: 2px solid var(--base-accent-color);
|
--base-focus: 2px solid var(--base-accent-color);
|
||||||
--base-outline-offset: 0.25em;
|
--base-outline-offset: 0.25em;
|
||||||
@@ -158,6 +158,35 @@ button, [type=button], [type=reset], [type=submit] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-top:0;
|
||||||
|
line-height:var(--header-line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-bottom:var(--base-spacing);
|
||||||
|
font-size:var(--large-font-size)
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-bottom:var(--base-spacing);
|
||||||
|
font-size:var(--medium-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom:calc(var(--base-spacing)/2);
|
||||||
|
font-size:var(--smedium-font-size)
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-bottom:calc(var(--base-spacing)/3);
|
||||||
|
font-size:var(--base-font-size)
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin:0 0 var(--base-spacing)
|
||||||
|
}
|
||||||
|
|
||||||
// ok actual styles
|
// ok actual styles
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -208,8 +237,11 @@ button, [type=submit] {
|
|||||||
|
|
||||||
article {
|
article {
|
||||||
background-color: var(--secondary-background-color);
|
background-color: var(--secondary-background-color);
|
||||||
}
|
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-color: var(--tertiary-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -241,14 +273,23 @@ article {
|
|||||||
|
|
||||||
article {
|
article {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
||||||
|
// grid-template-rows:
|
||||||
|
// 1fr min-content min-content;
|
||||||
|
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"title img"
|
"title img"
|
||||||
|
"details details"
|
||||||
"remove remove";
|
"remove remove";
|
||||||
|
|
||||||
button {
|
button {
|
||||||
grid-area: remove
|
grid-area: remove
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
grid-area: details;
|
||||||
|
}
|
||||||
|
|
||||||
padding: var(--base-spacing);
|
padding: var(--base-spacing);
|
||||||
margin: var(--base-spacing-half);
|
margin: var(--base-spacing-half);
|
||||||
}
|
}
|
||||||
@@ -318,7 +359,6 @@ header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.items {
|
.items {
|
||||||
// grid-column: 1 / -1;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(5, 1fr);
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
|
||||||
@@ -327,7 +367,11 @@ header {
|
|||||||
margin-bottom: var(--base-spacing);
|
margin-bottom: var(--base-spacing);
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
// grid-template-rows: 1fr 1fr 1fr;
|
|
||||||
|
&.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
background: var(--tertiary-background-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,6 +383,7 @@ header {
|
|||||||
|
|
||||||
.offer {
|
.offer {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
grid-template-columns: 1fr min-content;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"details img"
|
"details img"
|
||||||
"btn neg";
|
"btn neg";
|
||||||
@@ -358,7 +403,9 @@ header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
margin-top: var(--base-spacing-half);
|
||||||
grid-area: btn;
|
grid-area: btn;
|
||||||
|
border-color: var(--tertiary-background-color);
|
||||||
|
|
||||||
&.accepted {
|
&.accepted {
|
||||||
background: var(--color-accent-blue);
|
background: var(--color-accent-blue);
|
||||||
|
|||||||
Reference in new issue
Block a user