Skip to content

Instantly share code, notes, and snippets.

@AlexandroMtzG
Last active February 6, 2023 01:35
Show Gist options
  • Save AlexandroMtzG/2dba00f3446f8d89d819aa74fa8d5dca to your computer and use it in GitHub Desktop.
Save AlexandroMtzG/2dba00f3446f8d89d819aa74fa8d5dca to your computer and use it in GitHub Desktop.
import { Fragment } from "react";
import { DocumentDto } from "../dtos/DocumentDto";
import { DocumentTypeDto } from "../dtos/DocumentTypeDto";
interface Props {
year: number;
types: DocumentTypeDto[];
documents: DocumentDto[];
}
export default function AccountDocuments({ year, types, documents }: Props) {
function getDocumentsInPeriod(type: DocumentTypeDto, period: number): DocumentDto[] {
const yearDocuments = documents.filter((document) => document.year === year || !document.year);
const typeDocuments = yearDocuments.filter((document) => document.type.name === type.name);
const periodDocuments = typeDocuments.filter((document) => document.period === period || !document.period);
return periodDocuments;
}
function getDocumentTypePeriods(timesInYear: number | null): { from: Date; to: Date; name: string; number: number }[] {
let periods: { from: Date; to: Date; name: string; number: number }[] = [];
if (timesInYear === 1 !|| timesInYear) {
periods = [{ number: 1, name: "Jan-Dec", from: new Date(year, 0, 1), to: new Date(year, 11, 31) }];
} else if (timesInYear === 2) {
periods = [
{ number: 1, name: "Jan-Jun", from: new Date(year, 0, 1), to: new Date(year, 5, 30) },
{ number: 2, name: "Jul-Dec", from: new Date(year, 6, 1), to: new Date(year, 11, 31) },
];
} else if (timesInYear === 4) {
// jan-mar, apr-jun, jul-sep, oct-dec
periods = [
{ number: 1, name: "Jan-Mar", from: new Date(year, 0, 1), to: new Date(year, 2, 31) },
{ number: 2, name: "Apr-Jun", from: new Date(year, 3, 1), to: new Date(year, 5, 30) },
{ number: 3, name: "Jul-Sep", from: new Date(year, 6, 1), to: new Date(year, 8, 30) },
{ number: 4, name: "Oct-Dec", from: new Date(year, 9, 1), to: new Date(year, 11, 31) },
];
} else if (timesInYear === 6) {
// jan-feb, mar-apr, may-jun, jul-aug, sep-oct, nov-dec
periods = [
{ number: 1, name: "Jan-Feb", from: new Date(year, 0, 1), to: new Date(year, 1, 28) },
{ number: 2, name: "Mar-Apr", from: new Date(year, 2, 1), to: new Date(year, 3, 30) },
{ number: 3, name: "May-Jun", from: new Date(year, 4, 1), to: new Date(year, 5, 30) },
{ number: 4, name: "Jul-Aug", from: new Date(year, 6, 1), to: new Date(year, 7, 31) },
{ number: 5, name: "Sep-Oct", from: new Date(year, 8, 1), to: new Date(year, 9, 30) },
{ number: 6, name: "Nov-Dec", from: new Date(year, 10, 1), to: new Date(year, 11, 31) },
];
} else if (timesInYear === 12) {
periods = Array.from(Array(12).keys()).map((idx) => ({
number: idx + 1,
name: getMonthName(idx + 1),
from: new Date(year, idx, 1),
to: new Date(year, idx + 1, 0),
}));
}
return periods;
}
function getMonthName(month: number): string {
const months: string[] = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
if (month >= 1 && month <= 12) {
return months[month - 1].substring(0, 3);
}
return "";
}
function getDocumentTypeTimeline(type: DocumentTypeDto, period: number): "past" | "current" | "future" {
const today = new Date();
const periods = getDocumentTypePeriods(type.timesInYear);
const currentPeriod = periods.find((period) => period.from <= today && period.to >= today);
if (year < today.getFullYear()) {
return "past";
}
if (year > today.getFullYear()) {
return "future";
}
if (currentPeriod && currentPeriod.number === period) {
return "current";
} else if (currentPeriod && currentPeriod.number > period) {
return "past";
}
return "future";
}
function getDocumentInPeriodStatus(type: DocumentTypeDto, period: number): "valid" | "pending" | "n/a" | "missing" {
const documentsInPeriod = getDocumentsInPeriod(type, period);
const timeline = getDocumentTypeTimeline(type, period);
if (documentsInPeriod.length > 0) {
return "valid";
}
if (!type.timesInYear || timeline === "past") {
return "missing";
}
if (timeline === "future") {
return "n/a";
}
if (timeline === "current") {
return "pending";
}
return "missing";
}
function isCurrentMonth(month: number): boolean {
const today = new Date();
return today.getFullYear() === year && today.getMonth() + 1 === month;
}
return (
<div className="overflow-x-auto rounded-lg border border-gray-200 bg-white">
<table className="w-full">
<thead>
<tr>
<th rowSpan={2} className="border border-gray-200 bg-gray-100 text-gray-600 text-left text-sm font-bold px-4 py-2 w-full">
Document Type
</th>
{Array.from(Array(12).keys()).map((idx) => (
<Fragment key={idx}>
<th className="border border-gray-200 bg-gray-100 text-gray-600 text-center text-sm px-4 py-2 w-1/12">
<div className="flex">
<div className="font-bold">{getMonthName(idx + 1)}</div>
{isCurrentMonth(idx + 1) && (
<div className="text-xs text-theme-400 font-medium lowercase">
<span className="text-red-500 flex-shrink-0 inline-flex items-center p-1 rounded-full text-xs font-medium">
<svg className="h-2 w-2" fill="currentColor" viewBox="0 0 8 8">
<circle cx={4} cy={4} r={3} />
</svg>
</span>
</div>
)}
</div>
</th>
</Fragment>
))}
</tr>
</thead>
<tbody>
{types.map((type) => (
<tr key={type.name}>
<td className="border border-gray-200 text-gray-600 text-left text-sm px-4 py-2 truncate">{type.name}</td>
{getDocumentTypePeriods(type.timesInYear).map((_, idx) => (
<td key={idx} className="text-gray-600 text-center text-sm border" colSpan={12 / getDocumentTypePeriods(type.timesInYear).length}>
<DocumentPeriodStatus status={getDocumentInPeriodStatus(type, idx + 1)} />
{/* <TimelineBadge timeline={getDocumentTypeTimeline(type, idx + 1)} /> */}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
function DocumentPeriodStatus({ status }: { status: "valid" | "pending" | "n/a" | "missing" }) {
return (
<>
{status === "valid" && <div className="bg-teal-100 text-teal-800 text-xs font-bold w-full py-3 h-10">✓</div>}
{status === "missing" && <div className="bg-red-100 text-red-800 text-xs font-bold w-full py-3 h-10">✗</div>}
{status === "n/a" && <div className="bg-gray-200 text-gray-800 text-xs font-bold w-full py-3 h-10"></div>}
{status === "pending" && (
<div className="bg-white text-xs font-bold w-full py-3 h-10 flex justify-center text-gray-800">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4">
<path d="M9.25 13.25a.75.75 0 001.5 0V4.636l2.955 3.129a.75.75 0 001.09-1.03l-4.25-4.5a.75.75 0 00-1.09 0l-4.25 4.5a.75.75 0 101.09 1.03L9.25 4.636v8.614z" />
<path d="M3.5 12.75a.75.75 0 00-1.5 0v2.5A2.75 2.75 0 004.75 18h10.5A2.75 2.75 0 0018 15.25v-2.5a.75.75 0 00-1.5 0v2.5c0 .69-.56 1.25-1.25 1.25H4.75c-.69 0-1.25-.56-1.25-1.25v-2.5z" />
</svg>
</div>
)}
</>
);
}
function TimelineBadge({ timeline }: { timeline: "past" | "current" | "future" }) {
return (
<>
{timeline === "past" && <div className="bg-orange-100 text-orange-800 text-xs font-bold w-full h-10 py-3">Past</div>}
{timeline === "current" && <div className="bg-teal-100 text-teal-800 text-xs font-bold w-full h-10 py-3">Current</div>}
{timeline === "future" && <div className="bg-gray-100 text-gray-800 text-xs font-bold w-full h-10 py-3">Future</div>}
</>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment