bundle offers grouped
This commit is contained in:
1 parent
7946259550
commit
623d0f3938
12 files changed
+223
-143
No files matched your search
@@ -2,6 +2,8 @@
|
||||
|
||||
status stepper
|
||||
|
||||
queue up wantlist adds to gracefully handle rate limiting
|
||||
|
||||
push offers
|
||||
cover off admin routes and commands
|
||||
|
||||
|
||||
Generated
+30
-41
@@ -11,12 +11,11 @@
|
||||
"dependencies": {
|
||||
"@koa/cors": "^3.3.0",
|
||||
"@koa/router": "^12.0.0",
|
||||
"@prisma/client": "^4.1.0",
|
||||
"@prisma/client": "^4.5.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"bluebird": "^3.7.2",
|
||||
"bunyan": "^1.8.15",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"json-rpc-2.0": "^1.4.1",
|
||||
"koa": "^2.13.4",
|
||||
"koa-body": "^5.0.0",
|
||||
"koa-passport": "^5.0.0",
|
||||
@@ -35,7 +34,7 @@
|
||||
"axios": "^0.27.2",
|
||||
"mocha": "^10.0.0",
|
||||
"node-dev": "^7.4.3",
|
||||
"prisma": "^4.1.0",
|
||||
"prisma": "^4.5.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
@@ -136,12 +135,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.1.0.tgz",
|
||||
"integrity": "sha512-MvfPGAd42vHTiCYxwS6N+2U3F+ukoJ48D2QRnX1zSPJHBkh1CBtshl75daKzvVfgQwSouzSQeugKDej5di+E/g==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.5.0.tgz",
|
||||
"integrity": "sha512-B2cV0OPI1smhdYUxsJoLYQLoMlLH06MUxgFUWQnHodGMX98VRVXKmQE/9OcrTNkqtke5RC+YU24Szxd04tZA2g==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines-version": "4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8"
|
||||
"@prisma/engines-version": "4.5.0-43.0362da9eebca54d94c8ef5edd3b2e90af99ba452"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
@@ -156,16 +155,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.1.0.tgz",
|
||||
"integrity": "sha512-quqHXD3P83NBLVtRlts4SgKHmqgA8GMiyDTJ7af03Wg0gl6F5t65mBYvIuwmD+52vHm42JtIsp/fAO9YIV0JBA==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.5.0.tgz",
|
||||
"integrity": "sha512-4t9ir2SbQQr/wMCNU4YpHWp5hU14J2m3wHUZnGJPpmBF8YtkisxyVyQsKd1e6FyLTaGq8LOLhm6VLYHKqKNm+g==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8.tgz",
|
||||
"integrity": "sha512-cRRJwpHFGFJZvtHbY3GZjMffNBEjjZk68ztn+S2hDgPCGB4H66IK26roK94GJxBodSehwRJ0wGyebC2GoIH1JQ=="
|
||||
"version": "4.5.0-43.0362da9eebca54d94c8ef5edd3b2e90af99ba452",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.5.0-43.0362da9eebca54d94c8ef5edd3b2e90af99ba452.tgz",
|
||||
"integrity": "sha512-o7LyVx8PPJBLrEzLl6lpxxk2D5VnlM4Fwmrbq0NoT6pr5aa1OuHD9ZG+WJY6TlR/iD9bhmo2LNcxddCMr5Rv2A=="
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
@@ -1873,11 +1872,6 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/json-rpc-2.0": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.4.1.tgz",
|
||||
"integrity": "sha512-OX1NJhpIfuK4GjDnJ/gKtZy1HOYo0l4eL0a4rb0rNeQheX1xlyQ9+JMmPzs/sFNthpS/TXKPWlGo09X7B5l81A=="
|
||||
},
|
||||
"node_modules/keygrip": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
|
||||
@@ -2989,13 +2983,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.1.0.tgz",
|
||||
"integrity": "sha512-iwqpAT6In1uvMSwQAM3PqmaBdhh2OaQ/2t+n3RjpW4vAKP3R7E1T34FZUU4zGOWtMWm5dt0sPThQkT/h87r6gw==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.5.0.tgz",
|
||||
"integrity": "sha512-9Aeg4qiKlv9Wsjz4NO8k2CzRzlvS3A4FYVJ5+28sBBZ0eEwbiVOE/Jj7v6rZC1tFW2s4GSICQOAyuOjc6WsNew==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "4.1.0"
|
||||
"@prisma/engines": "4.5.0"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js",
|
||||
@@ -3790,23 +3784,23 @@
|
||||
}
|
||||
},
|
||||
"@prisma/client": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.1.0.tgz",
|
||||
"integrity": "sha512-MvfPGAd42vHTiCYxwS6N+2U3F+ukoJ48D2QRnX1zSPJHBkh1CBtshl75daKzvVfgQwSouzSQeugKDej5di+E/g==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.5.0.tgz",
|
||||
"integrity": "sha512-B2cV0OPI1smhdYUxsJoLYQLoMlLH06MUxgFUWQnHodGMX98VRVXKmQE/9OcrTNkqtke5RC+YU24Szxd04tZA2g==",
|
||||
"requires": {
|
||||
"@prisma/engines-version": "4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8"
|
||||
"@prisma/engines-version": "4.5.0-43.0362da9eebca54d94c8ef5edd3b2e90af99ba452"
|
||||
}
|
||||
},
|
||||
"@prisma/engines": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.1.0.tgz",
|
||||
"integrity": "sha512-quqHXD3P83NBLVtRlts4SgKHmqgA8GMiyDTJ7af03Wg0gl6F5t65mBYvIuwmD+52vHm42JtIsp/fAO9YIV0JBA==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.5.0.tgz",
|
||||
"integrity": "sha512-4t9ir2SbQQr/wMCNU4YpHWp5hU14J2m3wHUZnGJPpmBF8YtkisxyVyQsKd1e6FyLTaGq8LOLhm6VLYHKqKNm+g==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@prisma/engines-version": {
|
||||
"version": "4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.1.0-48.8d8414deb360336e4698a65aa45a1fbaf1ce13d8.tgz",
|
||||
"integrity": "sha512-cRRJwpHFGFJZvtHbY3GZjMffNBEjjZk68ztn+S2hDgPCGB4H66IK26roK94GJxBodSehwRJ0wGyebC2GoIH1JQ=="
|
||||
"version": "4.5.0-43.0362da9eebca54d94c8ef5edd3b2e90af99ba452",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.5.0-43.0362da9eebca54d94c8ef5edd3b2e90af99ba452.tgz",
|
||||
"integrity": "sha512-o7LyVx8PPJBLrEzLl6lpxxk2D5VnlM4Fwmrbq0NoT6pr5aa1OuHD9ZG+WJY6TlR/iD9bhmo2LNcxddCMr5Rv2A=="
|
||||
},
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
@@ -5100,11 +5094,6 @@
|
||||
"argparse": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"json-rpc-2.0": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.4.1.tgz",
|
||||
"integrity": "sha512-OX1NJhpIfuK4GjDnJ/gKtZy1HOYo0l4eL0a4rb0rNeQheX1xlyQ9+JMmPzs/sFNthpS/TXKPWlGo09X7B5l81A=="
|
||||
},
|
||||
"keygrip": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
|
||||
@@ -5939,12 +5928,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"prisma": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.1.0.tgz",
|
||||
"integrity": "sha512-iwqpAT6In1uvMSwQAM3PqmaBdhh2OaQ/2t+n3RjpW4vAKP3R7E1T34FZUU4zGOWtMWm5dt0sPThQkT/h87r6gw==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.5.0.tgz",
|
||||
"integrity": "sha512-9Aeg4qiKlv9Wsjz4NO8k2CzRzlvS3A4FYVJ5+28sBBZ0eEwbiVOE/Jj7v6rZC1tFW2s4GSICQOAyuOjc6WsNew==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@prisma/engines": "4.1.0"
|
||||
"@prisma/engines": "4.5.0"
|
||||
}
|
||||
},
|
||||
"qs": {
|
||||
|
||||
+2
-2
@@ -21,14 +21,14 @@
|
||||
"axios": "^0.27.2",
|
||||
"mocha": "^10.0.0",
|
||||
"node-dev": "^7.4.3",
|
||||
"prisma": "^4.1.0",
|
||||
"prisma": "^4.5.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@koa/cors": "^3.3.0",
|
||||
"@koa/router": "^12.0.0",
|
||||
"@prisma/client": "^4.1.0",
|
||||
"@prisma/client": "^4.5.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"bluebird": "^3.7.2",
|
||||
"bunyan": "^1.8.15",
|
||||
|
||||
@@ -65,6 +65,7 @@ model Offer {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
|
||||
status String
|
||||
condition String @default("")
|
||||
|
||||
item Decimal @db.Money
|
||||
// markup Decimal @db.Money
|
||||
|
||||
+1
-1
@@ -41,6 +41,7 @@ export default function createEvents(log: Log, pg: Pg, discogs, offers) {
|
||||
for (const want of wants) {
|
||||
const listings = await populateListings(want);
|
||||
log.info({ listings: listings.length, ...wantDetails(want) }, 'listings added');
|
||||
events.emit(Events.ListingsUpdated);
|
||||
}
|
||||
|
||||
log.info({ wants: wants.length }, 'finished syncing');
|
||||
@@ -50,7 +51,6 @@ export default function createEvents(log: Log, pg: Pg, discogs, offers) {
|
||||
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.on(Events.StartupSync, async function startupSync() {
|
||||
|
||||
+9
-1
@@ -27,7 +27,7 @@ export default function createOffers(log: Log, pg: Pg) {
|
||||
|
||||
// log.info({ listings }, 'creating offers for listings')
|
||||
|
||||
const shipping = 10 + Math.random(); // yolt
|
||||
const shipping = 7.5; // yolt
|
||||
|
||||
// const shipping = bulkShipping / listings.length;
|
||||
// how to deal with users who don't want all items in a bundle...
|
||||
@@ -41,6 +41,8 @@ export default function createOffers(log: Log, pg: Pg) {
|
||||
|
||||
status: Status.Pending,
|
||||
|
||||
condition: l.data.condition,
|
||||
|
||||
item: l.data.price.value,
|
||||
shipping,
|
||||
total: l.data.price.value + shipping,
|
||||
@@ -145,11 +147,17 @@ export default function createOffers(log: Log, pg: Pg) {
|
||||
return sellerOffers;
|
||||
}
|
||||
|
||||
async function userOffers(user: User) {
|
||||
const offers = await pg.userOffers(user);
|
||||
return groupBy(offers, (o) => o.wantId);
|
||||
}
|
||||
|
||||
return {
|
||||
generate,
|
||||
accept,
|
||||
bundles,
|
||||
statusSet,
|
||||
acpOffers,
|
||||
userOffers,
|
||||
};
|
||||
}
|
||||
+3
-3
@@ -150,7 +150,7 @@ export default function createPg(log: Log, prisma: PrismaClient): Pg {
|
||||
return prisma.$transaction(async (prisma) => {
|
||||
log.debug({ currentListingIds });
|
||||
|
||||
const openOfferStatuses = [Status.Pending, Status.Accepted];
|
||||
const protectedOfferStatuses = [Status.Confirmed, Status.Complete];
|
||||
|
||||
// can't delete a listing with an open offer
|
||||
// so delete those offers first
|
||||
@@ -161,7 +161,7 @@ export default function createPg(log: Log, prisma: PrismaClient): Pg {
|
||||
notIn: currentListingIds,
|
||||
},
|
||||
status: {
|
||||
in: openOfferStatuses,
|
||||
notIn: protectedOfferStatuses,
|
||||
},
|
||||
}
|
||||
});
|
||||
@@ -174,7 +174,7 @@ export default function createPg(log: Log, prisma: PrismaClient): Pg {
|
||||
where: {
|
||||
wantId: want.id,
|
||||
status: {
|
||||
notIn: openOfferStatuses,
|
||||
in: protectedOfferStatuses,
|
||||
}
|
||||
},
|
||||
select: {
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ export default function createRpc(log: Log, pg: Pg, user: User, ws, discogs, off
|
||||
}
|
||||
|
||||
async function userOffers(): Promise<OffersMessage> {
|
||||
return [RpcMessageType.Offers, await pg.userOffers(user)];
|
||||
return [RpcMessageType.Offers, await offers.userOffers(user)];
|
||||
}
|
||||
|
||||
async function userWants(): Promise<WantsMessage> {
|
||||
@@ -37,7 +37,7 @@ export default function createRpc(log: Log, pg: Pg, user: User, ws, discogs, off
|
||||
assert(Status[status], 'unknown status');
|
||||
|
||||
const offer = await offers.statusSet(user, id, status);
|
||||
const userOffers = await pg.userOffers(user);
|
||||
const userOffers = await offers.userOffers(user);
|
||||
|
||||
return [RpcMessageType.Offers, userOffers]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { h } from 'preact';
|
||||
import { ExternalLink, Check } from 'react-feather';
|
||||
import { useState } from 'preact/hooks';
|
||||
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { State } from './../store';
|
||||
|
||||
import http from '../http';
|
||||
|
||||
function OfferLine(offer) {
|
||||
const ws = useSelector((state: State) => state.ws);
|
||||
|
||||
function setStatus(status) {
|
||||
ws.sendOfferStatusSet(offer.id, status);
|
||||
}
|
||||
|
||||
const accepted = offer.status === 'Accepted';
|
||||
const classes = `${accepted ? 'accepted' : ''}`;
|
||||
const btnText = accepted
|
||||
? offer.status
|
||||
: 'Accept';
|
||||
|
||||
const negBtn = accepted
|
||||
? <button onClick={() => setStatus('Pending')} class='neg'>Cancel</button>
|
||||
: <button onClick={() => setStatus('Declined')} class='neg'>Decline</button>;
|
||||
|
||||
return (
|
||||
<div class="line">
|
||||
<span class='number'>${offer.total}</span>
|
||||
<button onClick={() => setStatus('Accepted')} disabled={accepted} class={classes} >{btnText}</button>
|
||||
{negBtn}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function OfferPriceBtn(offer) {
|
||||
const ws = useSelector((state: State) => state.ws);
|
||||
const accepted = offer.status === 'Accepted';
|
||||
const confirmed = offer.status === 'Confirmed';
|
||||
|
||||
function setStatus() {
|
||||
if (accepted) return ws.sendOfferStatusSet(offer.id, 'Pending')
|
||||
ws.sendOfferStatusSet(offer.id, 'Accepted');
|
||||
}
|
||||
|
||||
const classes = `${accepted ? 'accepted' : ''} ${confirmed ? 'confirmed' : ''}`;
|
||||
const btnText = accepted
|
||||
? offer.status
|
||||
: 'Accept';
|
||||
|
||||
return (
|
||||
<button disabled={confirmed} onClick={() => setStatus()} class={classes}>${offer.total} {offer.condition}</button>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default function WantOffers(wantOffers) {
|
||||
const ws = useSelector((state: State) => state.ws);
|
||||
|
||||
const details = wantOffers[0].want.data;
|
||||
|
||||
return (
|
||||
<div class="offer">
|
||||
<section>
|
||||
<h3>{details.artists_sort} - {details.title} <a href={details.uri} target="_blank"><ExternalLink /></a></h3>
|
||||
<div class='alt-text' >{details.year}</div>
|
||||
<div class='alt-text'>{details.formats.map(f => f.descriptions.join(' ')).join(' ')}</div>
|
||||
</section>
|
||||
<img src={details.thumb} />
|
||||
<div class="lines">
|
||||
{wantOffers.map(o => OfferPriceBtn(o))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -15,6 +15,7 @@ const Info = () => {
|
||||
<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> 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> Select all <span class='alt'>Offers</span> you are interested in, the more you accept the more likely the item will be <span class='alt-text'>Confirmed</span>.</p>
|
||||
<p> Check back regularly for updated <span class='alt'>Offers</span>, the global record marketplace is a living thing. </p>
|
||||
</section>
|
||||
|
||||
@@ -33,12 +34,8 @@ const Info = () => {
|
||||
|
||||
<section>
|
||||
<h2>FAQ</h2>
|
||||
<h3> Record Condition </h3>
|
||||
<p> All Mates Crates offers are for records that are either mint or very good condition. </p>
|
||||
<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>
|
||||
<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>
|
||||
<p> Please invite other collectors to the site. More users means offers can become cheaper and shipped sooner. </p>
|
||||
</section>
|
||||
|
||||
@@ -6,34 +6,23 @@ import { useSelector, useDispatch } from 'react-redux'
|
||||
import http from './../http';
|
||||
import { State, offersState } from './../store';
|
||||
|
||||
import Offer from './../components/offer';
|
||||
import WantOffers from './../components/want.offers';
|
||||
|
||||
function Home() {
|
||||
const dispatch = useDispatch()
|
||||
const offers = useSelector((state: State) => state.offers);
|
||||
|
||||
useEffect(() => {
|
||||
async function getOffers() {
|
||||
const [error, offers] = await http.getOffers();
|
||||
if (error) return console.error(error);
|
||||
dispatch(offersState.actions.set(offers));
|
||||
if (offers.length === 0) route('/search');
|
||||
}
|
||||
getOffers();
|
||||
}, []);
|
||||
|
||||
if (!offers.length) {
|
||||
return (
|
||||
<div class={'offers'}>
|
||||
loading...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// if (!offers.length) {
|
||||
// return (
|
||||
// <div class={'offers'}>
|
||||
// loading...
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
return (
|
||||
<div class={'offers'}>
|
||||
{offers.map(Offer)}
|
||||
{Object.values(offers).map(WantOffers)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+88
-69
@@ -56,7 +56,7 @@ html {
|
||||
font-family:var(--base-font-family);
|
||||
line-height:var(--base-line-height);
|
||||
-webkit-text-size-adjust:100%;
|
||||
background:var(--base-background-color);
|
||||
background:var(--background-0);
|
||||
box-sizing:border-box
|
||||
}
|
||||
|
||||
@@ -65,23 +65,6 @@ html {
|
||||
--breakpoint-medium: 35em;
|
||||
--breakpoint-large: 56em;
|
||||
|
||||
--color-dark-blue: #101014;
|
||||
--color-mid-blue: #16171d;
|
||||
--color-light-blue: #1f2128;
|
||||
--color-mid-grey: #9BA0B3;
|
||||
--color-off-white: #F4F1D0;
|
||||
--color-accent-blue: #5E9ED6;
|
||||
|
||||
--color-blue-gray: #506485;
|
||||
--color-green: #537a34;
|
||||
|
||||
--base-accent-color: var(--color-accent-blue);
|
||||
--fg-color: var(--color-off-white);
|
||||
--hover-link-color: var(--color-off-white);
|
||||
--base-background-color: var(--color-dark-blue);
|
||||
--secondary-background-color: var(--color-mid-blue);
|
||||
--tertiary-background-color: var(--color-light-blue);
|
||||
|
||||
--small-font-size: 0.75em;
|
||||
--base-font-size: 1em;
|
||||
--smedium-font-size: 1.333em;
|
||||
@@ -96,9 +79,6 @@ html {
|
||||
--base-line-height: 1.4;
|
||||
--header-line-height: 1.2;
|
||||
|
||||
--min-font-size: var(--base-font-size);
|
||||
--max-font-size: var(--large-font-size);
|
||||
|
||||
--base-border-color: var(--secondary-font-color);
|
||||
--base-border: 2px solid var(--base-border-color);
|
||||
--hover-border: 2px solid var(--base-font-color);
|
||||
@@ -107,11 +87,37 @@ html {
|
||||
--base-focus: 2px solid var(--base-accent-color);
|
||||
--base-outline-offset: 0.25em;
|
||||
|
||||
--base-spacing: 1rem;
|
||||
--base-spacing-half: calc(var(--base-spacing)/2);
|
||||
--base-spacing-double: calc(var(--base-spacing)*2);
|
||||
--min-font-size: var(--base-font-size);
|
||||
--max-font-size: var(--large-font-size);
|
||||
|
||||
--utility-bar-header-height: calc(var(--base-spacing) * 1.5);
|
||||
--color-dark-blue: #101014;
|
||||
--color-mid-blue: #16171d;
|
||||
--color-light-blue: #1f2128;
|
||||
--color-mid-grey: #9BA0B3;
|
||||
--color-off-white: #F4F1D0;
|
||||
--color-accent-blue: #5E9ED6;
|
||||
|
||||
--color-blue-gray: #506485;
|
||||
|
||||
--color-blue: #475977;
|
||||
--color-green: #476d4a;
|
||||
|
||||
--color-accepted: var(--color-blue);
|
||||
--color-confirmed: var(--color-green);
|
||||
|
||||
--base-accent-color: var(--color-accent-blue);
|
||||
--fg-color: var(--color-off-white);
|
||||
--hover-link-color: var(--color-off-white);
|
||||
|
||||
--background-0: var(--color-dark-blue);
|
||||
--background-1: var(--color-mid-blue);
|
||||
--background-2: var(--color-light-blue);
|
||||
|
||||
--spacing: 1rem;
|
||||
--spacing-half: calc(var(--spacing)/2);
|
||||
--spacing-double: calc(var(--spacing)*2);
|
||||
|
||||
--utility-bar-header-height: calc(var(--spacing) * 1.5);
|
||||
}
|
||||
|
||||
button, input, optgroup, select, textarea {
|
||||
@@ -136,11 +142,11 @@ input:focus, textarea:focus {
|
||||
}
|
||||
|
||||
button, input {
|
||||
overflow:visible
|
||||
overflow:visible;
|
||||
}
|
||||
|
||||
button, select {
|
||||
text-transform:none
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
button, [type=button], [type=reset], [type=submit] {
|
||||
@@ -154,7 +160,7 @@ button, [type=button], [type=reset], [type=submit] {
|
||||
text-transform: uppercase;
|
||||
|
||||
&:hover {
|
||||
background: var(--tertiary-background-color);
|
||||
background: var(--background-2);
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
@@ -165,33 +171,33 @@ h1, h2, h3, h4, h5, h6 {
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom:var(--base-spacing);
|
||||
margin-bottom:var(--spacing);
|
||||
font-size:var(--large-font-size)
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom:var(--base-spacing);
|
||||
margin-bottom:var(--spacing);
|
||||
font-size:var(--medium-font-size);
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom:calc(var(--base-spacing)/2);
|
||||
margin-bottom:calc(var(--spacing)/2);
|
||||
font-size:var(--smedium-font-size)
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom:calc(var(--base-spacing)/3);
|
||||
margin-bottom:calc(var(--spacing)/3);
|
||||
font-size:var(--base-font-size)
|
||||
}
|
||||
|
||||
p {
|
||||
margin:0 0 var(--base-spacing)
|
||||
margin:0 0 var(--spacing)
|
||||
}
|
||||
|
||||
// ok actual styles
|
||||
|
||||
body {
|
||||
padding-top: calc(var(--base-spacing)/2);
|
||||
padding-top: calc(var(--spacing)/2);
|
||||
}
|
||||
|
||||
main {
|
||||
@@ -199,8 +205,8 @@ main {
|
||||
}
|
||||
|
||||
button, [type=submit] {
|
||||
padding: var(--base-spacing-half);
|
||||
background: var(--secondary-background-color);
|
||||
padding: var(--spacing-half);
|
||||
background: var(--background-1);
|
||||
|
||||
}
|
||||
|
||||
@@ -222,7 +228,7 @@ button, [type=submit] {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
section {
|
||||
margin-bottom: var(--base-spacing);
|
||||
margin-bottom: var(--spacing);
|
||||
}
|
||||
|
||||
.contact {
|
||||
@@ -237,10 +243,10 @@ button, [type=submit] {
|
||||
}
|
||||
|
||||
article {
|
||||
background-color: var(--secondary-background-color);
|
||||
background-color: var(--background-1);
|
||||
|
||||
button {
|
||||
border-color: var(--tertiary-background-color);
|
||||
border-color: var(--background-2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,15 +256,15 @@ article {
|
||||
|
||||
article {
|
||||
display: grid;
|
||||
padding: var(--base-spacing);
|
||||
margin-bottom: var(--base-spacing);
|
||||
padding: var(--spacing);
|
||||
margin-bottom: var(--spacing);
|
||||
grid-template-columns: 1fr 4fr 1fr;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-right: var(--base-spacing);
|
||||
background: var(--tertiary-background-color);
|
||||
border-color: var(--secondary-background-color);
|
||||
margin-right: var(--spacing);
|
||||
background: var(--background-2);
|
||||
border-color: var(--background-1);
|
||||
|
||||
&.added {
|
||||
background: var(--color-green);
|
||||
@@ -270,7 +276,10 @@ article {
|
||||
.list {
|
||||
display: grid;
|
||||
grid-template-columns:
|
||||
1fr 1fr 1fr 1fr;
|
||||
1fr 1fr 1fr;
|
||||
|
||||
grid-row-gap: var(--spacing);
|
||||
grid-column-gap: var(--spacing);
|
||||
|
||||
article {
|
||||
display: grid;
|
||||
@@ -284,6 +293,7 @@ article {
|
||||
"remove remove";
|
||||
|
||||
button {
|
||||
margin-top: var(--spacing-half);
|
||||
grid-area: remove
|
||||
}
|
||||
|
||||
@@ -291,13 +301,12 @@ article {
|
||||
grid-area: details;
|
||||
}
|
||||
|
||||
padding: var(--base-spacing);
|
||||
margin: var(--base-spacing-half);
|
||||
padding: var(--spacing);
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: var(--base-spacing);
|
||||
margin-bottom: var(--spacing);
|
||||
|
||||
label, input {
|
||||
display: block;
|
||||
@@ -308,15 +317,15 @@ form {
|
||||
}
|
||||
|
||||
input {
|
||||
padding: var(--base-spacing-half);
|
||||
margin-bottom: var(--base-spacing);
|
||||
padding: var(--spacing-half);
|
||||
margin-bottom: var(--spacing);
|
||||
}
|
||||
|
||||
label {
|
||||
font-family: var(--secondary-font-family);
|
||||
font-size: var(--small-font-size);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: var(--base-spacing-half);
|
||||
margin-bottom: var(--spacing-half);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -334,7 +343,7 @@ a {
|
||||
nav {
|
||||
a {
|
||||
font-size: var(--small-font-size);
|
||||
margin-right: var(--base-spacing);
|
||||
margin-right: var(--spacing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +351,7 @@ nav {
|
||||
header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr min-content;
|
||||
padding-bottom: var(--base-spacing);
|
||||
padding-bottom: var(--spacing);
|
||||
|
||||
a {
|
||||
white-space: nowrap;
|
||||
@@ -353,10 +362,10 @@ header {
|
||||
flex-flow: row;
|
||||
|
||||
.bundle {
|
||||
margin-bottom: var(--base-spacing-double);
|
||||
margin-bottom: var(--spacing-double);
|
||||
|
||||
section {
|
||||
margin-bottom: var(--base-spacing);
|
||||
margin-bottom: var(--spacing);
|
||||
}
|
||||
|
||||
.items {
|
||||
@@ -364,14 +373,14 @@ header {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
|
||||
div {
|
||||
margin-right: var(--base-spacing);
|
||||
margin-bottom: var(--base-spacing);
|
||||
margin-right: var(--spacing);
|
||||
margin-bottom: var(--spacing);
|
||||
|
||||
display: grid;
|
||||
|
||||
&.bold {
|
||||
font-weight: bold;
|
||||
background: var(--tertiary-background-color);
|
||||
background: var(--background-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -385,13 +394,14 @@ header {
|
||||
.offer {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
grid-template-rows: 1fr min-content;
|
||||
grid-template-areas:
|
||||
"details img"
|
||||
"btn neg";
|
||||
"lines lines";
|
||||
|
||||
padding: var(--base-spacing);
|
||||
margin: var(--base-spacing-half);
|
||||
background: var(--secondary-background-color);
|
||||
padding: var(--spacing);
|
||||
margin: var(--spacing-half);
|
||||
background: var(--background-1);
|
||||
|
||||
section {
|
||||
grid-area: details;
|
||||
@@ -400,22 +410,31 @@ header {
|
||||
img {
|
||||
grid-area: img;
|
||||
justify-self: end;
|
||||
margin-left: var(--base-spacing-half);
|
||||
margin-left: var(--spacing-half);
|
||||
}
|
||||
|
||||
.lines {
|
||||
grid-area: lines;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: var(--spacing-half);
|
||||
|
||||
button {
|
||||
margin-top: var(--base-spacing-half);
|
||||
grid-area: btn;
|
||||
border-color: var(--tertiary-background-color);
|
||||
margin-top: var(--spacing-half);
|
||||
border-color: var(--background-2);
|
||||
|
||||
&.accepted {
|
||||
background: var(--color-accent-blue);
|
||||
background: var(--color-accepted);
|
||||
}
|
||||
|
||||
&.confirmed {
|
||||
cursor: auto;
|
||||
background: var(--color-confirmed);
|
||||
}
|
||||
|
||||
&.neg {
|
||||
margin-left: var(--base-spacing-half);
|
||||
grid-area: neg;
|
||||
margin-left: var(--spacing-half);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user