Add possibility to print charts, sort units

This commit is contained in:
NilsUeter
2025-04-06 11:01:31 +02:00
parent 4424356dbc
commit dca870edce
3 changed files with 193 additions and 158 deletions
+22 -32
View File
@@ -31,25 +31,14 @@ export const Roster = ({
position: "relative", position: "relative",
}} }}
> >
<div
className="print-display-none flex justify-between"
style={{
backgroundColor: "var(--primary-color)",
color: "#fff",
padding: "4px 16px",
fontSize: " 1.7em",
fontWeight: "800",
textTransform: "uppercase",
marginBottom: "12px",
}}
>
<span>{name}</span>
<span>{cost.points} pts</span>
</div>
{forces.map((force, index) => ( {forces.map((force, index) => (
<React.Fragment key={index}> <React.Fragment key={index}>
<ShortSummaryTable force={force} primaryColor={primaryColor} /> <ShortSummaryTable
force={force}
name={name}
points={cost.points}
primaryColor={primaryColor}
/>
<Force <Force
force={force} force={force}
onePerPage={onePerPage} onePerPage={onePerPage}
@@ -64,13 +53,22 @@ export const Roster = ({
const Force = ({ force, onePerPage, colorUserChoice }) => { const Force = ({ force, onePerPage, colorUserChoice }) => {
const { units, factionRules, rules, catalog } = force; const { units, factionRules, rules, catalog } = force;
var mergedRules = new Map([...factionRules, ...rules]); var mergedRules = new Map([...factionRules, ...rules]);
// units with role "Character" are sorted to the top, then "Battleline", then "Other"
const sortedUnits = units.sort((a, b) => {
if (a.role === "Character" && b.role !== "Character") return -1;
if (a.role !== "Character" && b.role === "Character") return 1;
if (a.role === "Battleline" && b.role !== "Battleline") return -1;
if (a.role !== "Battleline" && b.role === "Battleline") return 1;
return a.name.localeCompare(b.name);
});
return ( return (
<div <div
style={{ style={{
display: "contents", display: "contents",
}} }}
> >
{units.map((unit, index) => ( {sortedUnits.map((unit, index) => (
<Unit <Unit
key={unit.name + index} key={unit.name + index}
index={index} index={index}
@@ -226,7 +224,7 @@ const Unit = ({
: "", : "",
}} }}
> >
<div className="flex justify-end gap-3"> <div className="flex justify-end gap-3 pb-0.5">
<label <label
className="print-display-none" className="print-display-none"
style={{ style={{
@@ -390,15 +388,13 @@ const Unit = ({
}} }}
> >
{hasImage && <ImgEditor image={image} name={name} />} {hasImage && <ImgEditor image={image} name={name} />}
<div className="absolute right-[1px] top-[2px] flex gap-1"> <div className="absolute right-[1px] top-[3px] flex items-center gap-1.5">
{hasImage && !bgRemoved && ( {hasImage && !bgRemoved && (
<button <button
className="button print-display-none" className="button print-display-none border-none bg-[#f0f0f0e6] hover:bg-[#f0f0f0]"
style={{ style={{
border: "1px solid #999",
padding: "1px 4px", padding: "1px 4px",
fontSize: "0.8rem", fontSize: "0.8rem",
backgroundColor: "#f0f0f0",
}} }}
onClick={async () => { onClick={async () => {
// reset uploadRef // reset uploadRef
@@ -414,12 +410,10 @@ const Unit = ({
)} )}
{!hasImage && ( {!hasImage && (
<a <a
className="button print-display-none" className="button print-display-none border-none bg-[#f0f0f0e6] hover:bg-[#f0f0f0] hover:text-[#111113]"
style={{ style={{
border: "1px solid #999",
padding: "1px 4px", padding: "1px 4px",
fontSize: "0.8rem", fontSize: "0.8rem",
backgroundColor: "#f0f0f0",
}} }}
href={`https://www.google.com/search?udm=2&q=${name}+warhammer+40k+miniature`} href={`https://www.google.com/search?udm=2&q=${name}+warhammer+40k+miniature`}
target="_blank" target="_blank"
@@ -428,12 +422,10 @@ const Unit = ({
</a> </a>
)} )}
<label <label
className="button print-display-none" className="button print-display-none border-none bg-[#f0f0f0e6] hover:bg-[#f0f0f0]"
style={{ style={{
border: "1px solid #999",
padding: "1px 4px", padding: "1px 4px",
fontSize: "0.8rem", fontSize: "0.8rem",
backgroundColor: "#f0f0f0",
}} }}
> >
<input <input
@@ -465,12 +457,10 @@ const Unit = ({
</label> </label>
{hasImage && ( {hasImage && (
<button <button
className="button print-display-none" className="button print-display-none border-none bg-[#f0f0f0e6] hover:bg-[#f0f0f0]"
style={{ style={{
border: "1px solid #999",
padding: "1px 4px", padding: "1px 4px",
fontSize: "0.8rem", fontSize: "0.8rem",
backgroundColor: "#f0f0f0",
}} }}
onClick={() => { onClick={() => {
setImage(undefined); setImage(undefined);
+58 -15
View File
@@ -1,4 +1,4 @@
import React from "react"; import React, { useState } from "react";
import { Bar } from "react-chartjs-2"; import { Bar } from "react-chartjs-2";
import { import {
Chart as ChartJS, Chart as ChartJS,
@@ -20,7 +20,8 @@ ChartJS.register(
Title, Title,
); );
export const ShortSummaryTable = ({ force, primaryColor }) => { export const ShortSummaryTable = ({ force, primaryColor, name, points }) => {
const [hide, setHide] = useState(true);
const { units, factionRules, rules, catalog } = force; const { units, factionRules, rules, catalog } = force;
const sortedUnits = units.sort((a, b) => { const sortedUnits = units.sort((a, b) => {
@@ -104,8 +105,39 @@ export const ShortSummaryTable = ({ force, primaryColor }) => {
.sort((a, b) => a.save - b.save); .sort((a, b) => a.save - b.save);
return ( return (
<>
<div className="flex justify-end gap-3">
<label
className="print-display-none"
style={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
gap: 4,
userSelect: "none",
}}
>
<input type="checkbox" onChange={() => setHide(!hide)} />
<span className="print-display-none">Don't print this card.</span>
</label>
</div>
<div className={"flex flex-col " + (hide ? "print-display-none" : "")}>
<div <div
className="print-display-none my-4 -mt-4 flex flex-wrap items-start border-2 border-[var(--primary-color)]" className="flex justify-between"
style={{
backgroundColor: "var(--primary-color)",
color: "#fff",
padding: "4px 16px",
fontSize: " 1.7em",
fontWeight: "800",
textTransform: "uppercase",
}}
>
<span>{name}</span>
<span>{points} pts</span>
</div>
<div
className="mb-4 flex flex-wrap items-start border-2 border-[var(--primary-color)] print:flex-nowrap"
style={{ style={{
background: `url(${cardBackground})`, background: `url(${cardBackground})`,
backgroundSize: "cover", backgroundSize: "cover",
@@ -132,7 +164,7 @@ export const ShortSummaryTable = ({ force, primaryColor }) => {
fontWeight: 600, fontWeight: 600,
}} }}
> >
Cost (pts) Wounds
</div> </div>
<div <div
className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right" className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right"
@@ -142,7 +174,7 @@ export const ShortSummaryTable = ({ force, primaryColor }) => {
fontWeight: 600, fontWeight: 600,
}} }}
> >
Wounds Cost (pts)
</div> </div>
</div> </div>
</div> </div>
@@ -159,15 +191,15 @@ export const ShortSummaryTable = ({ force, primaryColor }) => {
{count > 1 ? count + "x " : ""} {count > 1 ? count + "x " : ""}
{name} {name}
</div> </div>
<div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1 text-right">
{cost.points} pts
</div>
<div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1 text-right"> <div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1 text-right">
{unit.modelStats?.reduce((sum, stat, index) => { {unit.modelStats?.reduce((sum, stat, index) => {
const count = unit.models?.[index]?.count || 1; const count = unit.models?.[index]?.count || 1;
return sum + (stat.wounds || 0) * count; return sum + (stat.wounds || 0) * count;
}, 0)} }, 0)}
</div> </div>
<div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1 text-right">
{cost.points} pts
</div>
</div> </div>
); );
})} })}
@@ -176,23 +208,28 @@ export const ShortSummaryTable = ({ force, primaryColor }) => {
Total Total
</div> </div>
<div className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right"> <div className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right">
{sortedUnits.reduce((sum, unit) => sum + unit.cost.points, 0)} pts {totalWoundCount}
</div> </div>
<div className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right"> <div className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right">
{totalWoundCount} {sortedUnits.reduce((sum, unit) => sum + unit.cost.points, 0)}{" "}
pts
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="flex w-full flex-col gap-2.5 border-[var(--primary-color)] p-4 md:w-[50%] md:flex-1 md:border-l-2"> <div className="flex w-full flex-col gap-2.5 border-[var(--primary-color)] p-4 pb-2 pt-3.5 md:w-[50%] md:flex-1 md:border-l-2 print:w-[100%]">
<ChartComponent <ChartComponent
data={{ data={{
labels: groupedChartDataMovement.map((data) => data.movement + '"'), labels: groupedChartDataMovement.map(
(data) => data.movement + '"',
),
datasets: [ datasets: [
{ {
label: "Total Cost (pts)", label: "Total Cost (pts)",
data: groupedChartDataMovement.map((data) => data.totalPoints), data: groupedChartDataMovement.map(
(data) => data.totalPoints,
),
backgroundColor: primaryColor, backgroundColor: primaryColor,
unitNames: groupedChartDataMovement.map( unitNames: groupedChartDataMovement.map(
(data) => data.unitNames, (data) => data.unitNames,
@@ -211,7 +248,9 @@ export const ShortSummaryTable = ({ force, primaryColor }) => {
datasets: [ datasets: [
{ {
label: "Total Cost (pts)", label: "Total Cost (pts)",
data: groupedChartDataToughness.map((data) => data.totalPoints), data: groupedChartDataToughness.map(
(data) => data.totalPoints,
),
backgroundColor: primaryColor, backgroundColor: primaryColor,
unitNames: groupedChartDataToughness.map( unitNames: groupedChartDataToughness.map(
(data) => data.unitNames, (data) => data.unitNames,
@@ -230,7 +269,9 @@ export const ShortSummaryTable = ({ force, primaryColor }) => {
label: "Total Cost (pts)", label: "Total Cost (pts)",
data: groupedChartDataSave.map((data) => data.totalPoints), data: groupedChartDataSave.map((data) => data.totalPoints),
backgroundColor: primaryColor, backgroundColor: primaryColor,
unitNames: groupedChartDataSave.map((data) => data.unitNames), unitNames: groupedChartDataSave.map(
(data) => data.unitNames,
),
}, },
], ],
}} }}
@@ -238,6 +279,8 @@ export const ShortSummaryTable = ({ force, primaryColor }) => {
/> />
</div> </div>
</div> </div>
</div>
</>
); );
}; };
+2
View File
@@ -69,6 +69,8 @@
button, button,
.button { .button {
display: flex;
align-items: center;
background-color: #f0f0f0; background-color: #f0f0f0;
color: #111113; color: #111113;
border-radius: 8px; border-radius: 8px;