Add possibility to print charts, sort units
This commit is contained in:
+22
-32
@@ -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);
|
||||||
|
|||||||
+169
-126
@@ -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,140 +105,182 @@ export const ShortSummaryTable = ({ force, primaryColor }) => {
|
|||||||
.sort((a, b) => a.save - b.save);
|
.sort((a, b) => a.save - b.save);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
className="print-display-none my-4 -mt-4 flex flex-wrap items-start border-2 border-[var(--primary-color)]"
|
<div className="flex justify-end gap-3">
|
||||||
style={{
|
<label
|
||||||
background: `url(${cardBackground})`,
|
className="print-display-none"
|
||||||
backgroundSize: "cover",
|
style={{
|
||||||
}}
|
display: "flex",
|
||||||
>
|
alignItems: "center",
|
||||||
<div className="mt-4 table w-full border-collapse overflow-hidden md:w-[50%] md:flex-1">
|
justifyContent: "flex-end",
|
||||||
<div className="table-header-group bg-[var(--primary-color)] font-bold uppercase text-white">
|
gap: 4,
|
||||||
<div className="table-row">
|
userSelect: "none",
|
||||||
<div
|
}}
|
||||||
className="table-cell border border-[var(--primary-color)] px-4 py-1"
|
>
|
||||||
style={{
|
<input type="checkbox" onChange={() => setHide(!hide)} />
|
||||||
fontSize: "1.1em",
|
<span className="print-display-none">Don't print this card.</span>
|
||||||
color: "#fff",
|
</label>
|
||||||
fontWeight: 600,
|
</div>
|
||||||
}}
|
<div className={"flex flex-col " + (hide ? "print-display-none" : "")}>
|
||||||
>
|
<div
|
||||||
Unit Name
|
className="flex justify-between"
|
||||||
</div>
|
style={{
|
||||||
<div
|
backgroundColor: "var(--primary-color)",
|
||||||
className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right"
|
color: "#fff",
|
||||||
style={{
|
padding: "4px 16px",
|
||||||
fontSize: "1.1em",
|
fontSize: " 1.7em",
|
||||||
color: "#fff",
|
fontWeight: "800",
|
||||||
fontWeight: 600,
|
textTransform: "uppercase",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cost (pts)
|
<span>{name}</span>
|
||||||
</div>
|
<span>{points} pts</span>
|
||||||
<div
|
|
||||||
className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right"
|
|
||||||
style={{
|
|
||||||
fontSize: "1.1em",
|
|
||||||
color: "#fff",
|
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Wounds
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="table-row-group" style={{ lineHeight: 1.05 }}>
|
<div
|
||||||
{sortedUnits.map((unit, index) => {
|
className="mb-4 flex flex-wrap items-start border-2 border-[var(--primary-color)] print:flex-nowrap"
|
||||||
let { name, cost, models } = unit;
|
style={{
|
||||||
const count = models?.[0]?.count || 1;
|
background: `url(${cardBackground})`,
|
||||||
return (
|
backgroundSize: "cover",
|
||||||
<div
|
}}
|
||||||
key={name + index}
|
>
|
||||||
className={`table-row ${index % 2 === 0 ? "" : "bg-[#c4c4c480]"}`}
|
<div className="mt-4 table w-full border-collapse overflow-hidden md:w-[50%] md:flex-1">
|
||||||
>
|
<div className="table-header-group bg-[var(--primary-color)] font-bold uppercase text-white">
|
||||||
<div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1">
|
<div className="table-row">
|
||||||
{count > 1 ? count + "x " : ""}
|
<div
|
||||||
{name}
|
className="table-cell border border-[var(--primary-color)] px-4 py-1"
|
||||||
|
style={{
|
||||||
|
fontSize: "1.1em",
|
||||||
|
color: "#fff",
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Unit Name
|
||||||
</div>
|
</div>
|
||||||
<div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1 text-right">
|
<div
|
||||||
{cost.points} pts
|
className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right"
|
||||||
|
style={{
|
||||||
|
fontSize: "1.1em",
|
||||||
|
color: "#fff",
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Wounds
|
||||||
</div>
|
</div>
|
||||||
<div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1 text-right">
|
<div
|
||||||
{unit.modelStats?.reduce((sum, stat, index) => {
|
className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right"
|
||||||
const count = unit.models?.[index]?.count || 1;
|
style={{
|
||||||
return sum + (stat.wounds || 0) * count;
|
fontSize: "1.1em",
|
||||||
}, 0)}
|
color: "#fff",
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cost (pts)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
})}
|
|
||||||
<div className="table-row bg-[var(--primary-color)] font-bold text-white">
|
|
||||||
<div className="table-cell border border-[var(--primary-color)] px-4 py-1">
|
|
||||||
Total
|
|
||||||
</div>
|
</div>
|
||||||
<div className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right">
|
<div className="table-row-group" style={{ lineHeight: 1.05 }}>
|
||||||
{sortedUnits.reduce((sum, unit) => sum + unit.cost.points, 0)} pts
|
{sortedUnits.map((unit, index) => {
|
||||||
</div>
|
let { name, cost, models } = unit;
|
||||||
<div className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right">
|
const count = models?.[0]?.count || 1;
|
||||||
{totalWoundCount}
|
return (
|
||||||
|
<div
|
||||||
|
key={name + index}
|
||||||
|
className={`table-row ${index % 2 === 0 ? "" : "bg-[#c4c4c480]"}`}
|
||||||
|
>
|
||||||
|
<div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1">
|
||||||
|
{count > 1 ? count + "x " : ""}
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
<div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1 text-right">
|
||||||
|
{unit.modelStats?.reduce((sum, stat, index) => {
|
||||||
|
const count = unit.models?.[index]?.count || 1;
|
||||||
|
return sum + (stat.wounds || 0) * count;
|
||||||
|
}, 0)}
|
||||||
|
</div>
|
||||||
|
<div className="table-cell border border-dotted border-[#9e9fa1] px-4 py-1 text-right">
|
||||||
|
{cost.points} pts
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<div className="table-row bg-[var(--primary-color)] font-bold text-white">
|
||||||
|
<div className="table-cell border border-[var(--primary-color)] px-4 py-1">
|
||||||
|
Total
|
||||||
|
</div>
|
||||||
|
<div className="table-cell border border-[var(--primary-color)] px-4 py-1 text-right">
|
||||||
|
{totalWoundCount}
|
||||||
|
</div>
|
||||||
|
<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
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<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
|
||||||
|
data={{
|
||||||
|
labels: groupedChartDataMovement.map(
|
||||||
|
(data) => data.movement + '"',
|
||||||
|
),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Total Cost (pts)",
|
||||||
|
data: groupedChartDataMovement.map(
|
||||||
|
(data) => data.totalPoints,
|
||||||
|
),
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
unitNames: groupedChartDataMovement.map(
|
||||||
|
(data) => data.unitNames,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
title="MOVEMENT OF YOUR UNITS"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ChartComponent
|
||||||
|
data={{
|
||||||
|
labels: groupedChartDataToughness.map(
|
||||||
|
(data) => data.toughness + "",
|
||||||
|
),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Total Cost (pts)",
|
||||||
|
data: groupedChartDataToughness.map(
|
||||||
|
(data) => data.totalPoints,
|
||||||
|
),
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
unitNames: groupedChartDataToughness.map(
|
||||||
|
(data) => data.unitNames,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
title="TOUGHNESS OF YOUR UNITS"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ChartComponent
|
||||||
|
data={{
|
||||||
|
labels: groupedChartDataSave.map((data) => data.save + "+"),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Total Cost (pts)",
|
||||||
|
data: groupedChartDataSave.map((data) => data.totalPoints),
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
unitNames: groupedChartDataSave.map(
|
||||||
|
(data) => data.unitNames,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
title="SAVE CHARACTERISTIC OF YOUR UNITS"
|
||||||
|
/>
|
||||||
|
</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">
|
|
||||||
<ChartComponent
|
|
||||||
data={{
|
|
||||||
labels: groupedChartDataMovement.map((data) => data.movement + '"'),
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "Total Cost (pts)",
|
|
||||||
data: groupedChartDataMovement.map((data) => data.totalPoints),
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
unitNames: groupedChartDataMovement.map(
|
|
||||||
(data) => data.unitNames,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
title="MOVEMENT OF YOUR UNITS"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ChartComponent
|
|
||||||
data={{
|
|
||||||
labels: groupedChartDataToughness.map(
|
|
||||||
(data) => data.toughness + "",
|
|
||||||
),
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "Total Cost (pts)",
|
|
||||||
data: groupedChartDataToughness.map((data) => data.totalPoints),
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
unitNames: groupedChartDataToughness.map(
|
|
||||||
(data) => data.unitNames,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
title="TOUGHNESS OF YOUR UNITS"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ChartComponent
|
|
||||||
data={{
|
|
||||||
labels: groupedChartDataSave.map((data) => data.save + "+"),
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "Total Cost (pts)",
|
|
||||||
data: groupedChartDataSave.map((data) => data.totalPoints),
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
unitNames: groupedChartDataSave.map((data) => data.unitNames),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
title="SAVE CHARACTERISTIC OF YOUR UNITS"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user