Skip to content

Commit

Permalink
Merge branch 'main' into side-nav-layout
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-tey authored Oct 7, 2024
2 parents 79d92e2 + 5fe0cac commit 833749e
Show file tree
Hide file tree
Showing 70 changed files with 2,054 additions and 3,520 deletions.
74 changes: 74 additions & 0 deletions .github/workflows/apply-issue-labels-to-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: "Apply issue labels to PR"

on:
pull_request_target:
types:
- opened

jobs:
label_on_pr:
runs-on: ubuntu-latest

permissions:
contents: none
issues: read
pull-requests: write

steps:
- name: Apply labels from linked issue to PR
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
async function getLinkedIssues(owner, repo, prNumber) {
const query = `query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 10) {
nodes {
number
labels(first: 10) {
nodes {
name
}
}
}
}
}
}
}`;
const variables = {
owner: owner,
repo: repo,
prNumber: prNumber,
};
const result = await github.graphql(query, variables);
return result.repository.pullRequest.closingIssuesReferences.nodes;
}
const pr = context.payload.pull_request;
const linkedIssues = await getLinkedIssues(
context.repo.owner,
context.repo.repo,
pr.number
);
const labelsToAdd = new Set();
for (const issue of linkedIssues) {
if (issue.labels && issue.labels.nodes) {
for (const label of issue.labels.nodes) {
labelsToAdd.add(label.name);
}
}
}
if (labelsToAdd.size) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: Array.from(labelsToAdd),
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ export default function WorkspaceTagsClient() {
const { AddEditTagModal, AddTagButton } = useAddEditTagModal();

const { tags, loading } = useTags();
const { data: tagsCount } = useLinksCount({
const { data: tagsCount } = useLinksCount<
{
tagId: string;
_count: number;
}[]
>({
groupBy: "tagId",
showArchived: true,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export function Form() {
data={{ url, title: "", description: "" }}
saving={isSubmitting}
loading={loading}
domains={domains}
onboarding
/>
)}
/>
Expand Down
30 changes: 30 additions & 0 deletions apps/web/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { FileX2 } from "lucide-react";
import Link from "next/link";

export default function NotFound() {
return (
<div className="my-10 flex flex-col items-center justify-center rounded-md bg-white py-12">
<div className="rounded-full bg-gray-100 p-3">
<FileX2 className="h-6 w-6 text-gray-600" />
</div>
<h1 className="my-3 text-xl font-semibold text-gray-700">
Page Not Found
</h1>
<p className="z-10 max-w-sm text-center text-sm text-gray-600">
Sorry, we couldn’t find the page you’re looking for.
</p>
<img
src="/_static/illustrations/coffee-call.svg"
alt="No links yet"
width={400}
height={400}
/>
<Link
href="/"
className="z-10 rounded-md border border-black bg-black px-10 py-2 text-sm font-medium text-white transition-all duration-75 hover:bg-white hover:text-black"
>
Back to home
</Link>
</div>
);
}
33 changes: 19 additions & 14 deletions apps/web/lib/actions/send-otp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,32 +45,37 @@ export const sendOtpAction = actionClient
);
}

if (email.includes("+") && email.endsWith("@gmail.com")) {
throw new Error(
"Email addresses with + are not allowed. Please use your work email instead.",
);
}

const code = generateOTP();

await Promise.all([
prisma.emailVerificationToken.deleteMany({
where: {
identifier: email,
},
}),
await prisma.emailVerificationToken.deleteMany({
where: {
identifier: email,
},
});

await Promise.all([
prisma.emailVerificationToken.create({
data: {
identifier: email,
token: code,
expires: new Date(Date.now() + EMAIL_OTP_EXPIRY_IN * 1000),
},
}),
]);

await sendEmail({
subject: `${process.env.NEXT_PUBLIC_APP_NAME}: OTP to verify your account`,
email,
react: VerifyEmail({
sendEmail({
subject: `${process.env.NEXT_PUBLIC_APP_NAME}: OTP to verify your account`,
email,
code,
react: VerifyEmail({
email,
code,
}),
}),
});
]);

return { ok: true };
});
14 changes: 13 additions & 1 deletion apps/web/lib/analytics/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,12 @@ export const VALID_ANALYTICS_ENDPOINTS = [
"devices",
"browsers",
"os",
"trigger", // deprecated, but keeping for now for backwards compatibility
"triggers",
"referers",
"referer_urls",
"top_links",
"top_urls",
"trigger",
] as const;

export const SINGULAR_ANALYTICS_ENDPOINTS = {
Expand All @@ -122,6 +123,7 @@ export const SINGULAR_ANALYTICS_ENDPOINTS = {
referers: "referer",
referer_urls: "refererUrl",
os: "os",
triggers: "trigger",
};

export const VALID_ANALYTICS_FILTERS = [
Expand All @@ -136,6 +138,7 @@ export const VALID_ANALYTICS_FILTERS = [
"device",
"browser",
"os",
"trigger",
"referer",
"refererUrl",
"url",
Expand All @@ -144,6 +147,12 @@ export const VALID_ANALYTICS_FILTERS = [
"root",
];

export const TRIGGER_DISPLAY = {
qr: "QR Scan",
link: "Link Click",
};
export const TRIGGER_TYPES = ["qr", "link"] as const;

export const EVENT_TYPES = ["clicks", "leads", "sales"] as const;

export const ANALYTICS_VIEWS = ["default", "funnel"] as const;
Expand All @@ -161,6 +170,8 @@ export const OLD_ANALYTICS_ENDPOINTS = [
"browsers",
"browser",
"os",
"triggers",
"trigger",
"referers",
"referer",
"top_links",
Expand All @@ -175,6 +186,7 @@ export const OLD_TO_NEW_ANALYTICS_ENDPOINTS = {
device: "devices",
browser: "browsers",
os: "os",
trigger: "triggers",
referer: "referers",
top_links: "top_links",
top_urls: "top_urls",
Expand Down
15 changes: 15 additions & 0 deletions apps/web/lib/analytics/get-analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const getAnalytics = async (params: AnalyticsFilters) => {
interval,
start,
end,
qr,
trigger,
timezone = "UTC",
isDemo,
isDeprecatedClicksEndpoint = false,
Expand Down Expand Up @@ -47,6 +49,10 @@ export const getAnalytics = async (params: AnalyticsFilters) => {

let granularity: "minute" | "hour" | "day" | "month" = "day";

if (groupBy === "trigger") {
groupBy = "triggers";
}

if (start) {
start = new Date(start);
end = end ? new Date(end) : new Date(Date.now());
Expand All @@ -70,6 +76,14 @@ export const getAnalytics = async (params: AnalyticsFilters) => {
granularity = INTERVAL_DATA[interval].granularity;
}

if (trigger) {
if (trigger === "qr") {
qr = true;
} else if (trigger === "link") {
qr = false;
}
}

// Create a Tinybird pipe
const pipe = (isDemo ? tbDemo : tb).buildPipe({
pipe: `v1_${groupBy}`,
Expand All @@ -81,6 +95,7 @@ export const getAnalytics = async (params: AnalyticsFilters) => {
...params,
eventType: event,
workspaceId,
qr,
start: start.toISOString().replace("T", " ").replace("Z", ""),
end: end.toISOString().replace("T", " ").replace("Z", ""),
granularity,
Expand Down
20 changes: 19 additions & 1 deletion apps/web/lib/analytics/get-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ import { EventsFilters } from "./types";

// Fetch data for /api/events
export const getEvents = async (params: EventsFilters) => {
let { event: eventType, workspaceId, interval, start, end, isDemo } = params;
let {
event: eventType,
workspaceId,
interval,
start,
end,
qr,
trigger,
isDemo,
} = params;

if (start) {
start = new Date(start);
Expand All @@ -40,6 +49,14 @@ export const getEvents = async (params: EventsFilters) => {
end = new Date(Date.now());
}

if (trigger) {
if (trigger === "qr") {
qr = true;
} else if (trigger === "link") {
qr = false;
}
}

const pipe = (isDemo ? tbDemo : tb).buildPipe({
pipe: "v1_events",
parameters: eventsFilterTB,
Expand All @@ -55,6 +72,7 @@ export const getEvents = async (params: EventsFilters) => {
...params,
eventType,
workspaceId,
qr,
offset: (params.page - 1) * params.limit,
start: start.toISOString().replace("T", " ").replace("Z", ""),
end: end.toISOString().replace("T", " ").replace("Z", ""),
Expand Down
2 changes: 1 addition & 1 deletion apps/web/lib/analytics/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type AnalyticsView = (typeof ANALYTICS_VIEWS)[number];

export type LocationTabs = "countries" | "cities";
export type TopLinksTabs = "link" | "url";
export type DeviceTabs = "devices" | "browsers" | "os";
export type DeviceTabs = "devices" | "browsers" | "os" | "triggers";
export type RefererTabs = "referers" | "referer_urls";

export type AnalyticsFilters = z.infer<typeof analyticsQuerySchema> & {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/lib/api/links/bulk-create-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export async function bulkCreateLinks({
} else {
// if there are no tags, we can use createMany to create the links
await prisma.link.createMany({
data: links.map((link) => {
data: links.map(({ tagId, tagIds, tagNames, ...link }) => {
const { utm_source, utm_medium, utm_campaign, utm_term, utm_content } =
getParamsFromURL(link.url);

Expand Down
2 changes: 1 addition & 1 deletion apps/web/lib/api/links/get-links-count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export async function getLinksCount({
...(search && {
OR: [
{
key: { contains: search },
shortLink: { contains: search },
},
{
url: { contains: search },
Expand Down
9 changes: 9 additions & 0 deletions apps/web/lib/openapi/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const retrieveAnalytics: ZodOpenApiOperationObject = {
z
.array(analyticsResponse.timeseries)
.openapi({ title: "AnalyticsTimeseries" }),
z
.array(analyticsResponse.continents)
.openapi({ title: "AnalyticsContinents" }),
z
.array(analyticsResponse.countries)
.openapi({ title: "AnalyticsCountries" }),
Expand All @@ -37,9 +40,15 @@ const retrieveAnalytics: ZodOpenApiOperationObject = {
.array(analyticsResponse.browsers)
.openapi({ title: "AnalyticsBrowsers" }),
z.array(analyticsResponse.os).openapi({ title: "AnalyticsOS" }),
z
.array(analyticsResponse.triggers)
.openapi({ title: "AnalyticsTriggers" }),
z
.array(analyticsResponse.referers)
.openapi({ title: "AnalyticsReferers" }),
z
.array(analyticsResponse.referer_urls)
.openapi({ title: "AnalyticsRefererUrls" }),
z
.array(analyticsResponse.top_links)
.openapi({ title: "AnalyticsTopLinks" }),
Expand Down
4 changes: 2 additions & 2 deletions apps/web/lib/swr/use-links-count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import useWorkspace from "./use-workspace";

const partialQuerySchema = getLinksCountQuerySchema.partial();

export default function useLinksCount(
export default function useLinksCount<T = any>(
opts: z.infer<typeof partialQuerySchema> & { ignoreParams?: boolean } = {},
) {
const { id: workspaceId } = useWorkspace();
Expand Down Expand Up @@ -49,7 +49,7 @@ export default function useLinksCount(
);

return {
data,
data: data as T,
loading: !error && data === undefined,
error,
};
Expand Down
Loading

0 comments on commit 833749e

Please sign in to comment.