feat: migrate to Next.js for Mini App and enhance project structure

- Replaced the previous webapp with a new Mini App built using Next.js, improving performance and maintainability.
- Updated the `.gitignore` to exclude Next.js build artifacts and node modules.
- Revised documentation in `AGENTS.md`, `README.md`, and `architecture.md` to reflect the new Mini App structure and technology stack.
- Enhanced Dockerfile to support the new build process for the Next.js application.
- Updated CI workflow to build and test the Next.js application.
- Added new configuration options for the Mini App, including `MINI_APP_SHORT_NAME` for improved deep linking.
- Refactored frontend testing setup to accommodate the new structure and testing framework.
- Removed legacy webapp files and dependencies to streamline the project.
This commit is contained in:
2026-03-03 16:04:08 +03:00
parent 2de5c1cb81
commit 16bf1a1043
148 changed files with 20240 additions and 7270 deletions

View File

@@ -0,0 +1,70 @@
/**
* Unit tests for DayIndicators: rounding is position-based (first / last / only child),
* not by indicator type, so multiple segments form one pill.
*/
import { describe, it, expect } from "vitest";
import { render } from "@testing-library/react";
import { DayIndicators } from "./DayIndicators";
const baseProps = {
dutyCount: 0,
unavailableCount: 0,
vacationCount: 0,
hasEvents: false,
};
describe("DayIndicators", () => {
it("renders nothing when no indicators", () => {
const { container } = render(<DayIndicators {...baseProps} />);
expect(container.firstChild).toBeNull();
});
it("renders one segment with only-child rounding (e.g. vacation only)", () => {
const { container } = render(
<DayIndicators {...baseProps} vacationCount={1} />
);
const wrapper = container.querySelector("[aria-hidden]");
expect(wrapper).toBeInTheDocument();
expect(wrapper?.className).toContain("[&>:only-child]:rounded-full");
const spans = wrapper?.querySelectorAll("span");
expect(spans).toHaveLength(1);
});
it("renders two segments with positional rounding (duty + vacation)", () => {
const { container } = render(
<DayIndicators {...baseProps} dutyCount={1} vacationCount={1} />
);
const wrapper = container.querySelector("[aria-hidden]");
expect(wrapper).toBeInTheDocument();
expect(wrapper?.className).toContain(
"[&>:first-child:not(:only-child)]:rounded-l-[3px]"
);
expect(wrapper?.className).toContain(
"[&>:last-child:not(:only-child)]:rounded-r-[3px]"
);
const spans = wrapper?.querySelectorAll("span");
expect(spans).toHaveLength(2);
});
it("renders three segments with first left-rounded, last right-rounded (duty + unavailable + vacation)", () => {
const { container } = render(
<DayIndicators
{...baseProps}
dutyCount={1}
unavailableCount={1}
vacationCount={1}
/>
);
const wrapper = container.querySelector("[aria-hidden]");
expect(wrapper).toBeInTheDocument();
expect(wrapper?.className).toContain(
"[&>:first-child:not(:only-child)]:rounded-l-[3px]"
);
expect(wrapper?.className).toContain(
"[&>:last-child:not(:only-child)]:rounded-r-[3px]"
);
const spans = wrapper?.querySelectorAll("span");
expect(spans).toHaveLength(3);
});
});