Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ee77ee5c1 | |||
| 3f34c7951f | |||
| dc87b3ad97 |
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2.1.3] - 2025-03-07
|
||||
|
||||
(No changes documented; release for version sync.)
|
||||
|
||||
## [2.1.2] - 2025-03-06
|
||||
|
||||
(No changes documented; release for version sync.)
|
||||
@@ -68,7 +72,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Input validation and initData hash verification for Miniapp access.
|
||||
- Optional CORS and init_data_max_age; use env for secrets.
|
||||
|
||||
[Unreleased]: https://github.com/your-org/duty-teller/compare/v2.1.2...HEAD
|
||||
[Unreleased]: https://github.com/your-org/duty-teller/compare/v2.1.3...HEAD
|
||||
[2.1.3]: https://github.com/your-org/duty-teller/releases/tag/v2.1.3
|
||||
[2.1.2]: https://github.com/your-org/duty-teller/releases/tag/v2.1.2
|
||||
[2.1.1]: https://github.com/your-org/duty-teller/releases/tag/v2.1.1 <!-- placeholder: set to your repo URL when publishing -->
|
||||
[2.0.6]: https://github.com/your-org/duty-teller/releases/tag/v2.0.6 <!-- placeholder: set to your repo URL when publishing -->
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "duty-teller"
|
||||
version = "2.1.2"
|
||||
version = "2.1.3"
|
||||
description = "Telegram bot for team duty shift calendar and group reminder"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
--today-hover: color-mix(in srgb, var(--bg) 15%, var(--today));
|
||||
--today-border: color-mix(in srgb, var(--today) 35%, transparent);
|
||||
--today-border-selected: color-mix(in srgb, var(--bg) 50%, transparent);
|
||||
--today-holiday-outline: color-mix(in srgb, var(--bg) 28%, var(--today));
|
||||
--today-gradient-end: color-mix(in srgb, var(--today) 15%, transparent);
|
||||
--muted-fade: color-mix(in srgb, var(--muted) 40%, transparent);
|
||||
--handle-bg: color-mix(in srgb, var(--muted) 80%, var(--text));
|
||||
|
||||
@@ -76,4 +76,20 @@ describe("CalendarDay", () => {
|
||||
const button = screen.getByRole("button", { name: /15/ });
|
||||
expect(button.getAttribute("aria-disabled")).not.toBe("true");
|
||||
});
|
||||
|
||||
it("applies today base and holiday outline when isToday and eventSummaries are set", () => {
|
||||
render(
|
||||
<CalendarDay
|
||||
{...defaultProps}
|
||||
isOtherMonth={false}
|
||||
isToday={true}
|
||||
eventSummaries={["Holiday"]}
|
||||
onDayClick={() => {}}
|
||||
/>
|
||||
);
|
||||
const button = screen.getByRole("button", { name: /15/ });
|
||||
expect(button.className).toMatch(/bg-today|today/);
|
||||
expect(button.className).toMatch(/ring-2|today-holiday-outline/);
|
||||
expect(button.getAttribute("aria-disabled")).not.toBe("true");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -45,6 +45,7 @@ function CalendarDayInner({
|
||||
[duties]
|
||||
);
|
||||
const hasEvent = eventSummaries.length > 0;
|
||||
const isTodayHoliday = isToday && hasEvent;
|
||||
const showIndicator = !isOtherMonth;
|
||||
const hasAny = duties.length > 0 || hasEvent;
|
||||
|
||||
@@ -82,10 +83,9 @@ function CalendarDayInner({
|
||||
showIndicator && hasAny && "font-bold",
|
||||
showIndicator &&
|
||||
hasEvent &&
|
||||
!isToday &&
|
||||
"bg-[linear-gradient(135deg,var(--surface)_0%,var(--today-gradient-end)_100%)] border border-[var(--today-border)]",
|
||||
isToday &&
|
||||
hasEvent &&
|
||||
"bg-today text-[var(--bg)] border border-[var(--today-border-selected)]"
|
||||
isTodayHoliday && "ring-1 ring-inset ring-[var(--today-holiday-outline)]"
|
||||
)}
|
||||
onClick={(e) => {
|
||||
if (isOtherMonth) return;
|
||||
|
||||
@@ -184,9 +184,9 @@ describe("ContactLinks", () => {
|
||||
expect(triggerHapticLightMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows Copied text in tooltip after successful copy", async () => {
|
||||
it("shows Copied via button aria-label and no tooltip after successful copy", async () => {
|
||||
copyToClipboardMock.mockResolvedValue(true);
|
||||
renderWithTooltip(
|
||||
render(
|
||||
<ContactLinks
|
||||
phone="+79991234567"
|
||||
username={null}
|
||||
@@ -201,10 +201,44 @@ describe("ContactLinks", () => {
|
||||
fireEvent.click(copyBtn);
|
||||
});
|
||||
|
||||
const tooltip = await screen.findByRole("tooltip", {
|
||||
name: /Copied|Скопировано/i,
|
||||
expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: /Copied|Скопировано/i })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
expect(tooltip).toBeInTheDocument();
|
||||
|
||||
it("reverts first copy button to Copy icon when copying the other field", async () => {
|
||||
copyToClipboardMock.mockResolvedValue(true);
|
||||
render(
|
||||
<ContactLinks
|
||||
phone="+79991234567"
|
||||
username="alice_dev"
|
||||
layout="block"
|
||||
showCopyButtons
|
||||
/>
|
||||
);
|
||||
const copyPhoneBtn = screen.getByRole("button", {
|
||||
name: /Copy phone number|Скопировать номер/i,
|
||||
});
|
||||
const copyTelegramBtn = screen.getByRole("button", {
|
||||
name: /Copy Telegram username|Скопировать логин Telegram/i,
|
||||
});
|
||||
await act(async () => {
|
||||
fireEvent.click(copyPhoneBtn);
|
||||
});
|
||||
expect(
|
||||
screen.getByRole("button", { name: /Copied|Скопировано/i })
|
||||
).toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(copyTelegramBtn);
|
||||
});
|
||||
expect(
|
||||
screen.getByRole("button", { name: /Copy phone number|Скопировать номер/i })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: /Copied|Скопировано/i })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show copy buttons when showCopyButtons is false", () => {
|
||||
|
||||
@@ -12,14 +12,8 @@ import { openPhoneLink } from "@/lib/open-phone-link";
|
||||
import { openTelegramProfile } from "@/lib/telegram-link";
|
||||
import { triggerHapticLight } from "@/lib/telegram-haptic";
|
||||
import { copyToClipboard } from "@/lib/copy-to-clipboard";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Phone as PhoneIcon, Send as TelegramIcon, Copy } from "lucide-react";
|
||||
import { Phone as PhoneIcon, Send as TelegramIcon, Copy, Check } from "lucide-react";
|
||||
|
||||
const COPIED_RESET_MS = 1800;
|
||||
|
||||
@@ -128,30 +122,22 @@ export function ContactLinks({
|
||||
<span className="truncate">{formatPhoneDisplay(phone!)}</span>
|
||||
</a>
|
||||
{showCopy && (
|
||||
<Tooltip
|
||||
open={copiedKind === "phone"}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) setCopiedKind(null);
|
||||
}}
|
||||
>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-12 w-12 shrink-0 items-center justify-center rounded-r-[calc(theme(borderRadius.md)-1px)] text-accent hover:bg-accent/15 focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-[-2px]"
|
||||
aria-label={t("contact.copy_phone")}
|
||||
aria-label={copiedKind === "phone" ? t("contact.copied") : t("contact.copy_phone")}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
void handleCopyPhone(e);
|
||||
}}
|
||||
>
|
||||
{copiedKind === "phone" ? (
|
||||
<Check className="size-5" aria-hidden />
|
||||
) : (
|
||||
<Copy className="size-5" aria-hidden />
|
||||
)}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" sideOffset={8}>
|
||||
{t("contact.copied")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -173,30 +159,22 @@ export function ContactLinks({
|
||||
<span className="truncate">@{cleanUsername}</span>
|
||||
</a>
|
||||
{showCopy && (
|
||||
<Tooltip
|
||||
open={copiedKind === "telegram"}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) setCopiedKind(null);
|
||||
}}
|
||||
>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-12 w-12 shrink-0 items-center justify-center rounded-r-[calc(theme(borderRadius.md)-1px)] text-accent hover:bg-accent/15 focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-[-2px]"
|
||||
aria-label={t("contact.copy_telegram")}
|
||||
aria-label={copiedKind === "telegram" ? t("contact.copied") : t("contact.copy_telegram")}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
void handleCopyTelegram(e);
|
||||
}}
|
||||
>
|
||||
{copiedKind === "telegram" ? (
|
||||
<Check className="size-5" aria-hidden />
|
||||
) : (
|
||||
<Copy className="size-5" aria-hidden />
|
||||
)}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" sideOffset={8}>
|
||||
{t("contact.copied")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user