Fixed remaning linting issues

This commit is contained in:
Luke Vella 2024-10-13 17:24:24 +01:00
parent a13df41bae
commit b7765013ca
No known key found for this signature in database
GPG key ID: 469CAD687F0D784C
33 changed files with 140 additions and 1495 deletions

View file

@ -78,7 +78,10 @@ export const TimesShownIn = () => {
return (
<ClockPreferences>
<button className="inline-flex items-center gap-x-2.5 text-sm hover:underline">
<button
type="button"
className="inline-flex items-center gap-x-2.5 text-sm hover:underline"
>
<GlobeIcon className="size-4" />
<Trans
i18nKey="timeShownIn"

View file

@ -1,23 +1,27 @@
/* eslint-disable */
// @ts-nocheck
import { DateLocalizer } from "react-big-calendar";
const weekRangeFormat = ({ start, end }, culture, local) =>
// biome-ignore lint/style/useTemplate: <explanation>
local.format(start, "MMMM DD", culture) +
" " +
" - " +
local.format(end, local.eq(start, end, "month") ? "DD" : "MMMM DD", culture);
const dateRangeFormat = ({ start, end }, culture, local) =>
local.format(start, "L", culture) + " " + local.format(end, "L", culture);
// biome-ignore lint/style/useTemplate: <explanation>
local.format(start, "L", culture) + " - " + local.format(end, "L", culture);
const timeRangeFormat = ({ start, end }, culture, local) =>
local.format(start, "LT", culture) + " " + local.format(end, "LT", culture);
// biome-ignore lint/style/useTemplate: <explanation>
local.format(start, "LT", culture) + " - " + local.format(end, "LT", culture);
const timeRangeStartFormat = ({ start }, culture, local) =>
local.format(start, "LT", culture) + " ";
// biome-ignore lint/style/useTemplate: <explanation>
local.format(start, "LT", culture) + " - ";
const timeRangeEndFormat = ({ end }, culture, local) =>
" " + local.format(end, "LT", culture);
// biome-ignore lint/style/useTemplate: <explanation>
" - " + local.format(end, "LT", culture);
export const formats = {
dateFormat: "DD",
@ -62,6 +66,7 @@ export default function (dayjs) {
return [dtA, dtB, datePart];
}
// biome-ignore lint/style/useDefaultParameterLast: <explanation>
function startOf(date = null, unit) {
const datePart = fixUnit(unit);
if (datePart) {
@ -70,6 +75,7 @@ export default function (dayjs) {
return dayjs(date).toDate();
}
// biome-ignore lint/style/useDefaultParameterLast: <explanation>
function endOf(date = null, unit) {
const datePart = fixUnit(unit);
if (datePart) {

View file

@ -70,7 +70,7 @@ const PollOptionsForm = ({
[views, watchView],
);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
// biome-ignore lint/style/noNonNullAssertion: <explanation>
const watchOptions = watch("options", [])!;
const watchDuration = watch("duration");
const watchTimeZone = watch("timeZone");

View file

@ -51,14 +51,14 @@ const SettingTitle = ({
const Setting = ({ children }: React.PropsWithChildren) => {
return (
<label
<div
className={cn(
"cursor-pointer bg-white hover:bg-gray-50 active:bg-gray-100",
"flex select-none justify-between gap-x-4 gap-y-2.5 rounded-md border p-3 sm:flex-row ",
)}
>
{children}
</label>
</div>
);
};

View file

@ -7,7 +7,7 @@ export type NewEventData = PollDetailsData &
PollOptionsData &
PollSettingsFormData;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
export interface PollFormProps<T extends Record<string, any>> {
onSubmit?: (data: T) => void;
onChange?: (data: Partial<T>) => void;

View file

@ -80,9 +80,9 @@ export const InviteDialog = () => {
</DialogDescription>
</DialogHeader>
<div className="min-w-0">
<label className="mb-2">
<div className="mb-2">
<Trans i18nKey="inviteLink" defaults="Invite Link" />
</label>
</div>
<div className="flex gap-2">
<CopyInviteLinkButton />
<div className="shrink-0">

View file

@ -77,7 +77,7 @@ const ModalProvider: React.FunctionComponent<ModalProviderProps> = ({
))}
{modals.map((props, i) => (
<Modal
key={i}
key={`modal-${i}-${props.title}`}
visible={true}
{...props}
content={

View file

@ -161,7 +161,7 @@ export const NewParticipantForm = (props: NewParticipantModalProps) => {
) : null}
</fieldset>
<fieldset>
<label className="mb-1 text-gray-500">{t("response")}</label>
<div className="mb-1 text-sm text-gray-500">{t("response")}</div>
<VoteSummary votes={props.votes} />
</fieldset>
{formState.errors.root?.message ? (

View file

@ -303,7 +303,7 @@ const DesktopPoll: React.FunctionComponent = () => {
? visibleParticipants.map((participant, i) => {
return (
<ParticipantRow
key={i}
key={participant.id}
participant={{
id: participant.id,
name: participant.name,

View file

@ -19,7 +19,7 @@ import { YouAvatar } from "@/components/poll/you-avatar";
import { Trans } from "@/components/trans";
import { usePoll } from "../../poll-context";
import { toggleVote, VoteSelector } from "../vote-selector";
import { VoteSelector, toggleVote } from "../vote-selector";
export interface ParticipantRowFormProps {
name?: string;
@ -112,24 +112,32 @@ const ParticipantRowForm = ({
<Controller
control={form.control}
name={`votes.${i}`}
render={({ field }) => (
<div
onClick={() => {
field.onChange({
optionId,
type: toggleVote(field.value?.type),
});
}}
className="absolute inset-0 flex cursor-pointer items-center justify-center hover:bg-gray-100 active:bg-gray-200/50 active:ring-1 active:ring-inset active:ring-gray-200"
>
<VoteSelector
value={field.value?.type}
onChange={(vote) => {
field.onChange({ optionId, type: vote });
render={({ field }) => {
const handleChange = () => {
field.onChange({
optionId,
type: toggleVote(field.value?.type),
});
};
return (
<div
onClick={handleChange}
onKeyDown={(e) => {
if (e.key === "Enter") {
handleChange();
}
}}
/>
</div>
)}
className="absolute inset-0 flex cursor-pointer items-center justify-center hover:bg-gray-100 active:bg-gray-200/50 active:ring-1 active:ring-inset active:ring-gray-200"
>
<VoteSelector
value={field.value?.type}
onChange={(vote) => {
field.onChange({ optionId, type: vote });
}}
/>
</div>
);
}}
/>
</td>
);

View file

@ -68,7 +68,7 @@ export const ParticipantRowView: React.FunctionComponent<{
{votes.map((vote, i) => {
return (
<td
key={i}
key={`vote-${i}-${vote}`}
className={cn(
"h-12 border-l border-t",
!vote || vote === "no" ? "bg-gray-100" : "bg-white",
@ -109,7 +109,7 @@ const ParticipantRow: React.FunctionComponent<ParticipantRowProps> = ({
const { user, ownsObject } = useUser();
const { getVote, optionIds } = usePoll();
const isYou = !!(user && ownsObject(participant) );
const isYou = !!(user && ownsObject(participant));
const { canEditParticipant } = usePermissions();
const canEdit = canEditParticipant(participant.id);

View file

@ -51,19 +51,19 @@ const useScoreByOptionId = () => {
return React.useMemo(() => {
const scoreByOptionId: Record<string, OptionScore> = {};
options.forEach((option) => {
for (const option of options) {
scoreByOptionId[option.id] = {
yes: [],
ifNeedBe: [],
no: [],
};
});
}
responses?.forEach((response) => {
response.votes.forEach((vote) => {
for (const response of responses) {
for (const vote of response.votes) {
scoreByOptionId[vote.optionId]?.[vote.type].push(response.id);
});
});
}
}
return scoreByOptionId;
}, [responses, options]);

View file

@ -48,8 +48,8 @@ const PollOptionVoteSummary: React.FunctionComponent<{ optionId: string }> = ({
) : (
<div className="grid grid-cols-2 gap-2">
<div className="col-span-1 space-y-2.5">
{participantsWhoVotedYes.map(({ name }, i) => (
<div key={i} className="flex">
{participantsWhoVotedYes.map(({ id, name }) => (
<div key={id} className="flex">
<div className="relative mr-2.5 flex size-5 items-center justify-center">
<OptimizedAvatarImage size="xs" name={name} />
<VoteIcon
@ -61,8 +61,8 @@ const PollOptionVoteSummary: React.FunctionComponent<{ optionId: string }> = ({
<div className="truncate text-sm">{name}</div>
</div>
))}
{participantsWhoVotedIfNeedBe.map(({ name }, i) => (
<div key={i} className="flex">
{participantsWhoVotedIfNeedBe.map(({ id, name }) => (
<div key={id} className="flex">
<div className="relative mr-2.5 flex size-5 items-center justify-center">
<OptimizedAvatarImage size="xs" name={name} />
<VoteIcon
@ -76,8 +76,8 @@ const PollOptionVoteSummary: React.FunctionComponent<{ optionId: string }> = ({
))}
</div>
<div className="col-span-1 space-y-2.5">
{participantsWhoVotedNo.map(({ name }, i) => (
<div key={i} className="flex">
{participantsWhoVotedNo.map(({ id, name }) => (
<div key={id} className="flex">
<div className="relative mr-2.5 flex size-5 items-center justify-center">
<OptimizedAvatarImage size="xs" name={name} />
<VoteIcon
@ -122,6 +122,17 @@ const PollOption: React.FunctionComponent<PollOptionProps> = ({
onClick={() => {
selectorRef.current?.click();
}}
onKeyUp={(e) => {
if (e.key === "Enter") {
selectorRef.current?.click();
}
}}
onKeyDown={(e) => {
if (e.key === " ") {
e.preventDefault();
selectorRef.current?.click();
}
}}
>
<div className="flex h-7 items-center justify-between gap-x-4">
<div className="shrink-0">{children}</div>

View file

@ -72,6 +72,14 @@ export function ScheduledEvent() {
const attendees = useAttendees();
const guestEmails: string[] = [];
for (const attendee of attendees) {
if (attendee.email) {
guestEmails.push(attendee.email);
}
}
if (!event) {
return null;
}
@ -116,9 +124,7 @@ export function ScheduledEvent() {
? { name: poll.user.name, email: poll.user.email }
: undefined
}
guests={attendees
.filter((participant) => !!participant.email)
.map((participant) => participant.email!)}
guests={guestEmails}
/>
</div>
</div>

View file

@ -106,7 +106,10 @@ const DateTimePreferencesForm = () => {
</SelectTrigger>
<SelectContent>
{dayjs.weekdays().map((day, index) => (
<SelectItem key={index} value={index.toString()}>
<SelectItem
key={index.toString()}
value={index.toString()}
>
{day}
</SelectItem>
))}

View file

@ -27,7 +27,7 @@ const Steps: React.FunctionComponent<StepsProps> = ({
{[...Array(total)].map((_, i) => {
return (
<span
key={i}
key={i.toString()}
className={cn("h-2 w-2 rounded-full transition-all", {
"bg-primary-400": i <= current,
"bg-gray-300": i > current,

View file

@ -20,6 +20,7 @@ export function DataTableColumnHeader<TData, TValue>({
return (
<button
type="button"
className="flex w-full items-center gap-x-2.5"
onClick={() => {
column.toggleSorting();

View file

@ -17,18 +17,18 @@ const TimeFormatPicker = ({
return (
<RadioGroup value={value} onValueChange={onChange} disabled={disabled}>
<div className="grid gap-y-1">
<label className="flex items-center gap-x-2">
<RadioGroupItem value="hours12" />
<span>
<div className="flex items-center gap-x-2">
<RadioGroupItem id="hours12" value="hours12" />
<label htmlFor="hours12">
<Trans i18nKey="12h" />
</span>
</label>
<label className="flex items-center gap-x-2">
<RadioGroupItem value="hours24" />
<span>
</label>
</div>
<div className="flex items-center gap-x-2">
<RadioGroupItem id="hours24" value="hours24" />
<label htmlFor="hours24">
<Trans i18nKey="24h" />
</span>
</label>
</label>
</div>
</div>
</RadioGroup>
);

View file

@ -1,6 +1,6 @@
import { Button } from "@rallly/ui/button";
import Link from "next/link";
import { Trans } from "next-i18next";
import Link from "next/link";
import type React from "react";
import { usePostHog } from "@/utils/posthog";
@ -39,7 +39,7 @@ export const UpgradeButton = ({
);
};
export const UpgradeLink = ({}) => {
export const UpgradeLink = () => {
return (
<Button variant="primary" asChild>
<Link href="/settings/billing">

View file

@ -14,7 +14,7 @@ export default function GlobalError({
}, [error]);
return (
<html>
<html lang="en">
<body>
{/* `NextError` is the default Next.js error page component. Its type
definition requires a `statusCode` prop. However, since the App Router

View file

@ -1,6 +1,6 @@
import crypto from "node:crypto";
// Original source: https://gist.github.com/dsumer/3594cda57e84a93a9019cddc71831882
import { prisma } from "@rallly/database";
import crypto from "node:crypto";
import type { NextApiRequest, NextApiResponse } from "next";
import * as Serialize from "php-serialize";
@ -55,7 +55,7 @@ export function validateWebhook(req: NextApiRequest) {
jsonObj = ksort(jsonObj);
for (const property in jsonObj) {
if (
jsonObj.hasOwnProperty(property) &&
Object.hasOwn(jsonObj, property) &&
typeof jsonObj[property] !== "string"
) {
if (Array.isArray(jsonObj[property])) {

View file

@ -18,7 +18,7 @@ function joinPath(baseUrl: string, subpath = "") {
export function objectToQueryString(obj: Record<string, string>) {
const parts = [];
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
if (Object.hasOwn(obj, key)) {
const value = obj[key];
if (value !== undefined) {
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);

View file

@ -1,6 +1,6 @@
import dayjs from "dayjs";
import { supportedTimeZones } from "@/utils/supported-time-zones";
import * as Sentry from "@sentry/nextjs";
import dayjs from "dayjs";
import type {
DateTimeOption,
@ -67,11 +67,20 @@ export function normalizeTimeZone(timeZone: string) {
return dayjs().tz(tz, true).utcOffset() === timeZoneOffset;
});
}
if (!tz) {
tz = supportedTimeZones.find((tz) => {
return getTimeZoneOffset(tz) === timeZoneOffset;
})!; // We assume there has to be a timezone with the same offset
});
if (!tz) {
Sentry.captureException(
new Error(`Failed to resolve timezone ${timeZone}`),
);
// TODO: This shouldn't happen since there is at least one timezone with the same offset
// that is supported by the application, but at least we have a fallback.
// We should prompt the user for their timezone if we can't resolve it automatically.
tz = "Europe/London";
}
}
return tz;

View file

@ -225,7 +225,7 @@ export const DayjsProvider: React.FunctionComponent<{
if (!localTime) {
return dayjs(date).tz(preferredTimeZone);
}
return dayjs(date).utc();
return dayjs(date).utc();
},
[preferredTimeZone],
);

View file

@ -3,7 +3,7 @@ export function getValueByPath<O extends Record<string, unknown>>(
path: string,
): unknown {
const pathArray = path.split(".");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
let curr: any = obj;
for (const part of pathArray) {
if (curr[part] === undefined) {

View file

@ -9,7 +9,7 @@
"useIgnoreFile": false
},
"linter": {
"ignore": ["node_modules", ".next", "dist"],
"ignore": ["node_modules", ".next", "dist", "public/static/scripts"],
"enabled": true,
"rules": {
"recommended": true

View file

@ -13,6 +13,7 @@
},
"dependencies": {
"@rallly/ui": "workspace:*",
"@rallly/database": "workspace:*",
"stripe": "^13.2.0",
"@radix-ui/react-radio-group": "^1.2.0",
"next": "*"

View file

@ -1,8 +1,8 @@
import { prisma } from "@rallly/database";
/**
* This script will go through all subscriptions and add the userId to the metadata.
*/
import { stripe } from "../lib/stripe";
import { prisma } from "@rallly/database";
async function getSubscriptionsWithMissingMetadata(
starting_after?: string,
@ -13,11 +13,11 @@ async function getSubscriptionsWithMissingMetadata(
limit: 100,
starting_after,
});
subscriptions.data.forEach((subscription) => {
for (const subscription of subscriptions.data) {
if (!subscription.metadata.userId) {
res.push(subscription.id);
}
});
}
if (subscriptions.has_more) {
return [
...res,
@ -26,7 +26,7 @@ async function getSubscriptionsWithMissingMetadata(
)),
];
}
return res;
return res;
}
async function normalizeSubscriptionMetadata() {

View file

@ -20,6 +20,7 @@ const prismaClientSingleton = () => {
export type ExtendedPrismaClient = ReturnType<typeof prismaClientSingleton>;
// biome-ignore lint/suspicious/noShadowRestrictedNames: https://www.prisma.io/docs/orm/more/help-and-troubleshooting/help-articles/nextjs-prisma-client-dev-practices
declare const globalThis: {
prismaGlobal: ExtendedPrismaClient;
} & typeof global;

View file

@ -15,6 +15,7 @@
"@rallly/tsconfig": "workspace:*",
"@types/node": "^18.15.10",
"prisma": "^5.17.0",
"dayjs": "^1.11.10",
"tsx": "^4.6.2"
},
"dependencies": {

View file

@ -160,9 +160,9 @@ async function main() {
await Promise.all(
[freeUser, proUser, proUserLegacy].map(async (user) => {
Array.from({ length: 20 }).forEach(async () => {
for (let i = 0; i < 20; i++) {
await createPollForUser(user.id);
});
}
console.info(`✓ Added ${user.email}`);
}),
);

View file

@ -1,6 +1,7 @@
import type React from "react";
type ComponentPropsAs<
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
C extends React.ElementType<any>,
T extends React.ComponentPropsWithoutRef<C>["as"],
> = Omit<

1420
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff