Add emoji flags and fix pile

This commit is contained in:
2023-10-05 00:43:19 +01:00
parent 5924de2d97
commit e13ef38e1a
16 changed files with 255 additions and 123 deletions
BIN
View File
Binary file not shown.
+53 -52
View File
@@ -1,61 +1,62 @@
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE Issuer (
iso TEXT,
code TEXT NOT NULL COLLATE NOCASE PRIMARY KEY,
name TEXT NOT NULL COLLATE NOCASE
);
INSERT INTO Issuer VALUES('algerie','Algeria');
INSERT INTO Issuer VALUES('andorre','Andorra');
INSERT INTO Issuer VALUES('argentine','Argentina');
INSERT INTO Issuer VALUES('australie','Australia');
INSERT INTO Issuer VALUES('autriche','Austria');
INSERT INTO Issuer VALUES('bahamas','Bahamas, The');
INSERT INTO Issuer VALUES('belgique','Belgium');
INSERT INTO Issuer VALUES('bolivie','Bolivia');
INSERT INTO Issuer VALUES('bresil','Brazil');
INSERT INTO Issuer VALUES('bulgarie','Bulgaria');
INSERT INTO Issuer VALUES('canada','Canada');
INSERT INTO Issuer VALUES('chili','Chile');
INSERT INTO Issuer VALUES('chine','China, People''s Republic of');
INSERT INTO Issuer VALUES('colombie','Colombia');
INSERT INTO Issuer VALUES('croatie','Croatia');
INSERT INTO Issuer VALUES('chypre','Cyprus');
INSERT INTO Issuer VALUES('republique_dominicaine','Dominican Republic');
INSERT INTO Issuer VALUES('equateur','Ecuador');
INSERT INTO Issuer VALUES('estonie','Estonia');
INSERT INTO Issuer VALUES('finlande','Finland');
INSERT INTO Issuer VALUES('france','France');
INSERT INTO Issuer VALUES('allemagne','Germany, Federal Republic of');
INSERT INTO Issuer VALUES('grece','Greece');
INSERT INTO Issuer VALUES('hongrie','Hungary');
INSERT INTO Issuer VALUES('irlande','Ireland');
INSERT INTO Issuer VALUES('italie','Italy');
INSERT INTO Issuer VALUES('japon','Japan');
INSERT INTO Issuer VALUES('liban','Lebanon');
INSERT INTO Issuer VALUES('lituanie','Lithuania');
INSERT INTO Issuer VALUES('luxembourg','Luxembourg');
INSERT INTO Issuer VALUES('malte','Malta');
INSERT INTO Issuer VALUES('mexique','Mexico');
INSERT INTO Issuer VALUES('maroc','Morocco');
INSERT INTO Issuer VALUES('nepal','Nepal');
INSERT INTO Issuer VALUES('pays-bas','Netherlands');
INSERT INTO Issuer VALUES('pologne','Poland');
INSERT INTO Issuer VALUES('portugal','Portugal');
INSERT INTO Issuer VALUES('roumanie','Romania');
INSERT INTO Issuer VALUES('russie','Russia');
INSERT INTO Issuer VALUES('ancienne_urss','Soviet Union');
INSERT INTO Issuer VALUES('slovaquie','Slovakia');
INSERT INTO Issuer VALUES('slovenie','Slovenia');
INSERT INTO Issuer VALUES('afrique_du_sud','South Africa');
INSERT INTO Issuer VALUES('espagne','Spain');
INSERT INTO Issuer VALUES('suede','Sweden');
INSERT INTO Issuer VALUES('suisse','Switzerland');
INSERT INTO Issuer VALUES('thailande','Thailand');
INSERT INTO Issuer VALUES('turquie','Turkey');
INSERT INTO Issuer VALUES('emirats_arabes_unis','United Arab Emirates');
INSERT INTO Issuer VALUES('royaume-uni','United Kingdom');
INSERT INTO Issuer VALUES('etats-unis','United States');
INSERT INTO Issuer VALUES('england','England');
INSERT INTO Issuer VALUES('DZ','algerie','Algeria');
INSERT INTO Issuer VALUES('AD','andorre','Andorra');
INSERT INTO Issuer VALUES('AR','argentine','Argentina');
INSERT INTO Issuer VALUES('AU','australie','Australia');
INSERT INTO Issuer VALUES('AT','autriche','Austria');
INSERT INTO Issuer VALUES('BS','bahamas','Bahamas, The');
INSERT INTO Issuer VALUES('BE','belgique','Belgium');
INSERT INTO Issuer VALUES('BO','bolivie','Bolivia');
INSERT INTO Issuer VALUES('BR','bresil','Brazil');
INSERT INTO Issuer VALUES('BG','bulgarie','Bulgaria');
INSERT INTO Issuer VALUES('CA','canada','Canada');
INSERT INTO Issuer VALUES('CL','chili','Chile');
INSERT INTO Issuer VALUES('CN','chine','China, People''s Republic of');
INSERT INTO Issuer VALUES('CO','colombie','Colombia');
INSERT INTO Issuer VALUES('HR','croatie','Croatia');
INSERT INTO Issuer VALUES('CY','chypre','Cyprus');
INSERT INTO Issuer VALUES('DO','republique_dominicaine','Dominican Republic');
INSERT INTO Issuer VALUES('EC','equateur','Ecuador');
INSERT INTO Issuer VALUES('EE','estonie','Estonia');
INSERT INTO Issuer VALUES('FI','finlande','Finland');
INSERT INTO Issuer VALUES('FR','france','France');
INSERT INTO Issuer VALUES('DE','allemagne','Germany, Federal Republic of');
INSERT INTO Issuer VALUES('GR','grece','Greece');
INSERT INTO Issuer VALUES('HU','hongrie','Hungary');
INSERT INTO Issuer VALUES('IE','irlande','Ireland');
INSERT INTO Issuer VALUES('IT','italie','Italy');
INSERT INTO Issuer VALUES('JP','japon','Japan');
INSERT INTO Issuer VALUES('LB','liban','Lebanon');
INSERT INTO Issuer VALUES('LT','lituanie','Lithuania');
INSERT INTO Issuer VALUES('LU','luxembourg','Luxembourg');
INSERT INTO Issuer VALUES('MT','malte','Malta');
INSERT INTO Issuer VALUES('MX','mexique','Mexico');
INSERT INTO Issuer VALUES('MA','maroc','Morocco');
INSERT INTO Issuer VALUES('NP','nepal','Nepal');
INSERT INTO Issuer VALUES('NL','pays-bas','Netherlands');
INSERT INTO Issuer VALUES('PL','pologne','Poland');
INSERT INTO Issuer VALUES('PT','portugal','Portugal');
INSERT INTO Issuer VALUES('RO','roumanie','Romania');
INSERT INTO Issuer VALUES('RU','russie','Russia');
INSERT INTO Issuer VALUES('SUHH','ancienne_urss','Soviet Union');
INSERT INTO Issuer VALUES('SK','slovaquie','Slovakia');
INSERT INTO Issuer VALUES('SI','slovenie','Slovenia');
INSERT INTO Issuer VALUES('ZA','afrique_du_sud','South Africa');
INSERT INTO Issuer VALUES('ES','espagne','Spain');
INSERT INTO Issuer VALUES('SE','suede','Sweden');
INSERT INTO Issuer VALUES('CH','suisse','Switzerland');
INSERT INTO Issuer VALUES('TH','thailande','Thailand');
INSERT INTO Issuer VALUES('TR','turquie','Turkey');
INSERT INTO Issuer VALUES('AE','emirats_arabes_unis','United Arab Emirates');
INSERT INTO Issuer VALUES('GB','royaume-uni','United Kingdom');
INSERT INTO Issuer VALUES('US','etats-unis','United States');
INSERT INTO Issuer VALUES('GB-ENG','england','England');
CREATE TABLE Currency (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL COLLATE NOCASE,
+1
View File
@@ -46,6 +46,7 @@ model Edge {
}
model Issuer {
iso String?
code String @id
name String
type NumistaType[]
+7 -10
View File
@@ -1,14 +1,11 @@
import { prisma } from "@/api";
export const getCountries = async (filter?: string) => {
return (
await prisma.issuer.groupBy({
by: "name",
...(filter && {
where: {
name: { contains: filter },
},
}),
})
).map(({ name }) => name);
return await prisma.issuer.findMany({
...(filter && {
where: {
name: { contains: filter },
},
}),
});
};
+25 -23
View File
@@ -3,11 +3,13 @@ import { Slider } from "primereact/slider";
import { InputNumber } from "primereact/inputnumber";
import { useRouter } from "next/router";
import { ParsedUrlQuery } from "querystring";
import { MultiSelectFilter } from "@/components/filters/multi-select";
import { MultiToggleFilter } from "@/components/filters/multi-toggle";
import { Sidebar, SidebarProps } from "primereact/sidebar";
import { MultiSelectCurrenciesFilter } from "@/components/filters/multi-select-currencies";
import { MultiSelectCountriesFilter } from "./multi-select-countries";
type Props = SidebarProps & {
filterBanknotes?: boolean;
query: ParsedUrlQuery;
};
@@ -171,7 +173,7 @@ const getDefaultParams = (query: ParsedUrlQuery) => {
};
};
export const Filters = ({ query, ...sidebarProps }: Props) => {
export const Filters = ({ query, filterBanknotes, ...sidebarProps }: Props) => {
const router = useRouter();
const [state, dispatch] = useReducer(reducer, getDefaultParams(query));
@@ -198,33 +200,33 @@ export const Filters = ({ query, ...sidebarProps }: Props) => {
return (
<Sidebar position={"right"} {...sidebarProps}>
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
{!filterBanknotes && (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<MultiToggleFilter
baseName="type"
items={[
{
name: "coins",
value: state.coins,
set: (payload) => dispatch({ key: "coins", payload }),
},
{
name: "banknotes",
value: state.banknotes,
set: (payload) => dispatch({ key: "banknotes", payload }),
},
]}
/>
</div>
)}
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<MultiToggleFilter
baseName="type"
items={[
{
name: "coins",
value: state.coins,
set: (payload) => dispatch({ key: "coins", payload }),
},
{
name: "banknotes",
value: state.banknotes,
set: (payload) => dispatch({ key: "banknotes", payload }),
},
]}
/>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<MultiSelectFilter
name={"countries"}
<MultiSelectCountriesFilter
selectedValues={state.countries}
toggleValue={(payload) => dispatch({ key: "country", payload })}
/>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<MultiSelectFilter
name={"currencies"}
<MultiSelectCurrenciesFilter
selectedValues={state.currencies}
toggleValue={(payload) => dispatch({ key: "currency", payload })}
/>
@@ -0,0 +1,68 @@
import { getFlagEmoji } from "@/core/utils/flags";
import { Issuer } from "@prisma/client";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { MultiSelect, MultiSelectProps } from "primereact/multiselect";
import { useMemo } from "react";
type Props = MultiSelectProps & {
selectedValues: string[];
toggleValue: (value: string) => void;
};
const Option = (name: string) => {
const queryClient = useQueryClient();
const country = queryClient
.getQueryData<Issuer[]>(["countries"])
?.find((country) => country.name === name);
return getFlagEmoji(country?.iso) + " " + name;
};
export const MultiSelectCountriesFilter = ({
selectedValues,
toggleValue,
...props
}: Props) => {
const { data } = useQuery<Issuer[]>({
queryKey: ["countries"],
queryFn: async () => {
const res = await fetch("/api/countries");
return await res.json();
},
});
const countries = useMemo(
() => data?.map(({ iso, name }) => getFlagEmoji(iso) + " " + name),
[data],
);
const selectedWithFlags = useMemo(
() =>
selectedValues.map(
(name) => {
const country = data?.find((country) => country.name === name);
return getFlagEmoji(country?.iso) + " " + name;
},
[data],
),
[data, selectedValues],
);
return (
<>
<label htmlFor={"countries"}>{"Countries"}</label>
<MultiSelect
name={"countries"}
display="chip"
value={selectedWithFlags}
onChange={(e) =>
toggleValue(e.selectedOption.split(" ").slice(1).join(" "))
}
itemTemplate={Option}
options={countries ?? selectedValues}
filter
maxSelectedLabels={4}
{...props}
/>
</>
);
};
@@ -1,36 +1,36 @@
import { useQuery } from "@tanstack/react-query";
import { MultiSelect } from "primereact/multiselect";
import { MultiSelect, MultiSelectProps } from "primereact/multiselect";
type Props = {
name: "currencies" | "countries";
export type Props = MultiSelectProps & {
selectedValues: string[];
toggleValue: (value: string) => void;
};
export const MultiSelectFilter = ({
name,
export const MultiSelectCurrenciesFilter = ({
selectedValues,
toggleValue,
...props
}: Props) => {
const { data } = useQuery<string[]>({
queryKey: [name],
queryKey: ["currencies"],
queryFn: async () => {
const res = await fetch("/api/" + name);
const res = await fetch("/api/currencies");
return await res.json();
},
});
return (
<>
<label htmlFor={name}>{name}</label>
<label htmlFor={"currencies"}>{"Currencies"}</label>
<MultiSelect
name={name}
name={"currencies"}
display="chip"
value={selectedValues}
onChange={(e) => toggleValue(e.selectedOption)}
options={data ?? []}
options={data ?? selectedValues}
filter
maxSelectedLabels={4}
{...props}
/>
</>
);
+1 -1
View File
@@ -9,7 +9,7 @@ type Props = {
export const Header = ({ route, onOpenDrawer }: Props) => {
const router = useRouter();
const searchParams = router.asPath.split("?")[1];
const searchParams = router.asPath.split("?")[1] ?? "";
return (
<div
style={{
+1 -1
View File
@@ -13,7 +13,7 @@ export const CoinInstances = (coinProps: NumistaType) => {
const [ref, { at }] = useCylinder(
() => ({
args: [size / 16, size / 16, thickness / 16, 8],
args: [(1 + size) / 16, (1 + size) / 16, thickness / 16, 6],
mass: weight,
position: [0, -3, 0],
}),
+59 -23
View File
@@ -1,12 +1,13 @@
import { Canvas, SpotLightProps } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import { useRef } from "react";
import { Suspense, useMemo, useRef } from "react";
import { CoinInstances } from "./coin-instances";
import { PlaneProps, Physics, usePlane } from "@react-three/cannon";
import { Mesh, Vector3 } from "three";
import styles from "./index.module.css";
import { StageLightModel } from "./stage-light-model";
import { useNumistaTypes } from "@/core/hooks/use-types";
import { Message } from "primereact/message";
const Plane = (props: PlaneProps) => {
const [ref] = usePlane(() => ({ ...props }), useRef<Mesh>(null));
@@ -46,30 +47,65 @@ const StageLight = (
export const Pile = () => {
const coins = useNumistaTypes({ filterBanknotes: true });
const count = useMemo(() => {
return coins?.reduce((total, coin) => total + (coin.count ?? 1), 0) ?? 0;
}, [coins]);
return (
<Canvas
shadows
camera={{ position: [0, 16, 16] }}
className={styles.canvas}
>
<StageLight position={[32, 16, 32]} />
<StageLight position={[32, 16, -32]} />
<StageLight position={[-32, 16, 32]} />
<StageLight position={[-32, 16, -32]} />
<Physics gravity={[0, -20, 0]}>
<Plane rotation={[-Math.PI / 2, 0, 0]} position={[0, -2, 0]} />
{coins?.map((coinProps) => (
<CoinInstances key={String(coinProps.id)} {...coinProps} />
))}
</Physics>
<>
{count > 200 && (
<div
style={{
position: "absolute",
zIndex: 1,
inset: "8px 64px auto",
maxWidth: "max-content",
margin: "auto",
}}
>
<Message
severity="error"
style={{
width: "max-content",
maxWidth: "calc(100vw - 128px)",
margin: "auto",
}}
text={
<>
{"Existem " + count + " moedas que cumprem estes requisitos."}
<br /> {"Faz uma pesquisa mais específica."}
</>
}
/>
</div>
)}
<OrbitControls
maxPolarAngle={Math.PI / 2}
enablePan={false}
minDistance={4}
maxDistance={96}
/>
</Canvas>
<Canvas
shadows
camera={{ position: [0, 16, 16] }}
className={styles.canvas}
>
<StageLight position={[32, 16, 32]} />
<StageLight position={[32, 16, -32]} />
<StageLight position={[-32, 16, 32]} />
<StageLight position={[-32, 16, -32]} />
<Physics gravity={[0, -20, 0]}>
<Plane rotation={[-Math.PI / 2, 0, 0]} position={[0, -2, 0]} />
{count < 200 &&
coins?.map((coinProps) => (
<Suspense fallback={null} key={coinProps.id}>
<CoinInstances {...coinProps} />
</Suspense>
))}
</Physics>
<OrbitControls
maxPolarAngle={Math.PI / 2}
enablePan={false}
minDistance={4}
maxDistance={96}
/>
</Canvas>
</>
);
};
+5 -1
View File
@@ -1,5 +1,6 @@
import { Button } from "primereact/button";
import { useShowcaseStore } from "./store";
import { getFlagEmoji } from "@/core/utils/flags";
export const InfoBox = () => {
const isSpinning = useShowcaseStore((state) => state.isSpinning);
@@ -51,7 +52,10 @@ export const InfoBox = () => {
color: "white",
}}
>
<small>{issuer?.name}</small>
<span>
{getFlagEmoji(issuer?.iso) + " "}
<small>{issuer?.name}</small>
</span>
<h1 style={{ textAlign: "center" }}>{title}</h1>
<p>{min_year !== max_year ? min_year + " - " + max_year : min_year}</p>
<small>{(count ?? 1) + " in collection"}</small>
+1 -1
View File
@@ -11,7 +11,7 @@ export const useNumistaTypes = (options?: {
const { data: coins } = useQuery<NumistaType[]>({
queryKey: ["types", searchParams],
queryFn: async () => {
const res = await fetch("/api/types?" + searchParams);
const res = await fetch("/api/types?" + searchParams ?? "");
const types = await res.json();
options?.onSuccess?.(types);
return types;
+19
View File
@@ -0,0 +1,19 @@
export function getFlagEmoji(countryCode?: string | null) {
if (!countryCode?.length) {
return "";
}
switch (countryCode) {
case "GB-ENG":
return "🏴󠁧󠁢󠁥󠁮󠁧󠁿";
case "SUHH":
return "☭";
default:
return String.fromCodePoint(
...countryCode
.toUpperCase()
.split("")
.map((char) => 127397 + char.charCodeAt(0)),
);
}
}
+2 -1
View File
@@ -2,6 +2,7 @@ import Head from "next/head";
import { GetStaticProps, NextPage } from "next";
import { getCountryCounts } from "@/api/countries/get-country-counts";
import Link from "next/link";
import { getFlagEmoji } from "@/core/utils/flags";
type Props = {
countryCounts: Awaited<ReturnType<typeof getCountryCounts>>;
@@ -30,7 +31,7 @@ const CountriesPage: NextPage<Props> = ({ countryCounts }) => {
{countryCounts.map((country) => (
<div key={country.code} style={{ maxWidth: 640, margin: "auto" }}>
<b style={{ textAlign: "center", display: "block" }}>
{country.name}
{getFlagEmoji(country.iso) + " " + country.name}
</b>
<div
+1
View File
@@ -28,6 +28,7 @@ const PilePage: NextPage<{ query: ParsedUrlQuery }> = ({ query }) => {
visible={drawerVisible}
onHide={() => setDrawerVisible(false)}
className={styles.sidebar}
filterBanknotes
query={query}
/>
+2
View File
@@ -144,6 +144,8 @@ export interface NumistaType {
}
interface Issuer {
/** @description ISO 3166-1 code of the issuer, sometimes -2 or -3 */
iso: string;
/** @description Unique ID of the issuer on Numista */
code: string;
/** @description Name of the issuer */