polish(stocktake-pdf): zebra rows, drop Location, Target→Max, prominent Status, wider Name
Visual layout cleanup on the Stocktake PDF: - Zebra-stripe alternating rows (subtle slate-50 on even rows, white on odd) so adjacent rows scan easily. - Drop the Location column — one less thing to fit on the page, and the operator has the /stock detail page for that. - Rename the Target column header to 'Max' (user-facing naming). - Move the Status column to the right edge and make it bold + colour-coded using STOCK_STATUS_STYLES[status].hex, matching the on-screen badge palette. - Widen the Name column (18%→24%) so full stock-item names read in one line for most rows. - Redistribute remaining widths so the table still totals 100% of the landscape A4 page width. Column widths now: SKU 10 / Name 24 / Supplier 18 / Current 9 / Min 8 / Max 9 / Status 16 / spacer 6 (= 100%). Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
6033aba18d
commit
a35e715b21
1 changed files with 42 additions and 27 deletions
|
|
@ -8,6 +8,9 @@ export interface StockItemStocktakeDocumentProps {
|
|||
generatedAtIso: string;
|
||||
}
|
||||
|
||||
const ZEBRA_EVEN = "#f8fafc"; // slate-50 — subtle alternating row tint
|
||||
const ZEBRA_ODD = "#ffffff";
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
page: { padding: 28, fontSize: 9, fontFamily: "Helvetica" },
|
||||
header: { marginBottom: 18 },
|
||||
|
|
@ -30,29 +33,40 @@ const styles = StyleSheet.create({
|
|||
paddingVertical: 4,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
trEven: {
|
||||
flexDirection: "row",
|
||||
backgroundColor: ZEBRA_EVEN,
|
||||
borderBottomWidth: 0.5,
|
||||
borderBottomColor: "#ddd",
|
||||
paddingVertical: 4,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
td: { fontSize: 9 },
|
||||
tdRight: { fontSize: 9, textAlign: "right" },
|
||||
// Columns mirror the /stock page table so a printed copy feels
|
||||
// familiar to the operator. Landscape A4 gives 8 columns room to
|
||||
// breathe — same allocation as the stock-orders list PDF.
|
||||
cSku: { width: "9%" },
|
||||
cName: { width: "18%" },
|
||||
cSupplier: { width: "16%" },
|
||||
cCurrent: { width: "8%" },
|
||||
cMin: { width: "7%" },
|
||||
cTarget: { width: "8%" },
|
||||
cLocation: { width: "14%" },
|
||||
cStatus: { width: "12%" },
|
||||
spacer: { width: "8%" },
|
||||
// Status is rendered with the status colour (from STOCK_STATUS_STYLES)
|
||||
// and bold, so it pops as a right-side summary of the row.
|
||||
tdStatus: { fontSize: 9, fontFamily: "Helvetica-Bold" },
|
||||
// Columns mirror the on-screen /stock table — Location dropped, Target
|
||||
// renamed to "Max". Name and Status widened so the row reads at a
|
||||
// glance. Landscape A4 has plenty of horizontal room.
|
||||
cSku: { width: "10%" },
|
||||
cName: { width: "24%" },
|
||||
cSupplier: { width: "18%" },
|
||||
cCurrent: { width: "9%" },
|
||||
cMin: { width: "8%" },
|
||||
cMax: { width: "9%" },
|
||||
cStatus: { width: "16%" },
|
||||
spacer: { width: "6%" },
|
||||
empty: { marginTop: 24, color: "#666", fontSize: 10, textAlign: "center" },
|
||||
});
|
||||
|
||||
/**
|
||||
* Stock list PDF — landscape A4, every active stock item in a
|
||||
* table sorted by name. Columns mirror the on-screen /stock table
|
||||
* (SKU, Name, Supplier, Current, Min, Target, Location, Status) so
|
||||
* the printed copy is recognisable. The Status column uses the same
|
||||
* `getStockStatus` derivation as the on-screen badge.
|
||||
* minus Location, with Target renamed to "Max" and the Name column
|
||||
* widened. Rows alternate subtle zebra tinting so adjacent rows are
|
||||
* easy to scan; the Status column sits at the right edge and is
|
||||
* bold + colour-coded against the on-screen badge palette.
|
||||
*/
|
||||
export function StockItemStocktakeDocument({
|
||||
items,
|
||||
|
|
@ -80,10 +94,9 @@ export function StockItemStocktakeDocument({
|
|||
<Text style={[styles.th, styles.cMin, { textAlign: "right" }]}>
|
||||
Min
|
||||
</Text>
|
||||
<Text style={[styles.th, styles.cTarget, { textAlign: "right" }]}>
|
||||
Target
|
||||
<Text style={[styles.th, styles.cMax, { textAlign: "right" }]}>
|
||||
Max
|
||||
</Text>
|
||||
<Text style={[styles.th, styles.cLocation]}>Location</Text>
|
||||
<Text style={[styles.th, styles.cStatus]}>Status</Text>
|
||||
<Text style={[styles.th, styles.spacer]} />
|
||||
</View>
|
||||
|
|
@ -92,21 +105,22 @@ export function StockItemStocktakeDocument({
|
|||
<Text style={styles.empty}>No active stock items.</Text>
|
||||
) : (
|
||||
<View style={styles.table}>
|
||||
{items.map((item) => {
|
||||
{items.map((item, idx) => {
|
||||
const status = getStockStatus(
|
||||
item.current_qty,
|
||||
item.min_qty,
|
||||
item.is_active,
|
||||
);
|
||||
const statusLabel = STOCK_STATUS_STYLES[status].label;
|
||||
const statusStyle = STOCK_STATUS_STYLES[status];
|
||||
const currentQty = Number(item.current_qty ?? 0);
|
||||
const minQty = Number(item.min_qty ?? 0);
|
||||
const targetQty =
|
||||
const maxQty =
|
||||
item.target_qty !== undefined && item.target_qty !== null
|
||||
? Number(item.target_qty)
|
||||
: null;
|
||||
const rowStyle = idx % 2 === 0 ? styles.trEven : styles.tr;
|
||||
return (
|
||||
<View key={item.id} style={styles.tr} wrap={false}>
|
||||
<View key={item.id} style={rowStyle} wrap={false}>
|
||||
<Text style={[styles.td, styles.cSku]}>{item.sku || "—"}</Text>
|
||||
<Text style={[styles.td, styles.cName]}>{item.name}</Text>
|
||||
<Text style={[styles.td, styles.cSupplier]}>
|
||||
|
|
@ -117,13 +131,14 @@ export function StockItemStocktakeDocument({
|
|||
{item.unit ? ` ${item.unit}` : ""}
|
||||
</Text>
|
||||
<Text style={[styles.tdRight, styles.cMin]}>{minQty}</Text>
|
||||
<Text style={[styles.tdRight, styles.cTarget]}>
|
||||
{targetQty !== null && targetQty > 0 ? targetQty : "—"}
|
||||
<Text style={[styles.tdRight, styles.cMax]}>
|
||||
{maxQty !== null && maxQty > 0 ? maxQty : "—"}
|
||||
</Text>
|
||||
<Text style={[styles.td, styles.cLocation]}>
|
||||
{item.location || "—"}
|
||||
<Text
|
||||
style={[styles.tdStatus, styles.cStatus, { color: statusStyle.hex }]}
|
||||
>
|
||||
{statusStyle.label}
|
||||
</Text>
|
||||
<Text style={[styles.td, styles.cStatus]}>{statusLabel}</Text>
|
||||
<Text style={[styles.td, styles.spacer]} />
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue