Skip to content

Commit af6db62

Browse files
committed
Just testing e2e flow
1 parent 4258829 commit af6db62

10 files changed

+457
-62
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ node_modules
33
/netlify/functions/server/build
44
/public/build
55
/.netlify
6+
7+
.env

app/root.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export default function App() {
2727
<Meta />
2828
<Links />
2929
</head>
30-
<body className="bg-lime-50 p-8 m-4 text-black">
30+
<body className="bg-lime-50 px-16 py-4 mx-16 my-4 text-black">
3131
<Outlet />
3232
<ScrollRestoration />
3333
<Scripts />

app/routes/app.tsx

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { LoaderFunction, useLoaderData, useTransition } from "remix";
2+
import { ChartProvider, XAxis, BarSeries, YAxis, Tooltip } from "rough-charts";
3+
import { mockResponse } from "~/mocks/mock-response";
4+
import { Article } from "~/types/articleData";
5+
import { ArticleProcessedData } from "~/types/articleProcessedData";
6+
import { cookie } from "~/utils/cookie.server";
7+
import {
8+
processArticlesStats,
9+
articlesMapByMonth,
10+
findMonthWithHeighestArticles,
11+
} from "~/utils/processArticles";
12+
13+
export const loader: LoaderFunction = async ({ request, params }) => {
14+
const value = request.headers.get("Cookie");
15+
const cookieHeader = await cookie.parse(value);
16+
17+
const { apiKey } = cookieHeader;
18+
19+
const response = await fetch("https://dev.to/api/articles/me/published", {
20+
headers: {
21+
"api-key": apiKey,
22+
},
23+
});
24+
25+
const data = await response.json();
26+
27+
const articles: Article[] = data;
28+
29+
const filteredArticles = articles.filter((article) => {
30+
const year = article.published_at.slice(0, 4);
31+
return year === "2021";
32+
});
33+
34+
const articleProcessedData: ArticleProcessedData = {
35+
stats: processArticlesStats(filteredArticles),
36+
monthWiseGraphData: articlesMapByMonth(filteredArticles),
37+
monthHighArticles: findMonthWithHeighestArticles(filteredArticles),
38+
filteredArticles,
39+
};
40+
41+
return articleProcessedData;
42+
};
43+
44+
export default function AppRoute() {
45+
const articleProcessedData = useLoaderData<ArticleProcessedData>();
46+
const transition = useTransition();
47+
48+
if (transition.state === "loading") {
49+
return (
50+
<div className="text-center py-4 underline decoration-cyan-300 text-2xl mb-4 font-semibold">
51+
Dev.to Wrapped 2021
52+
<div>Loading!</div>
53+
</div>
54+
);
55+
}
56+
57+
const stats = articleProcessedData.stats;
58+
59+
return (
60+
<div>
61+
<h1 className="text-center py-4 underline decoration-cyan-300 text-2xl mb-4 font-semibold">
62+
Dev.to Wrapped 2021
63+
</h1>
64+
<div className="border-solid rounded-lg my-8 p-8 shadow-lg shadow-lime-100/50 bg-lime-100 flex flex-row justify-between">
65+
<div className="text-xl underline decoration-sky-500 decoration-2">
66+
{`You have written a total of ${stats.totalArticles} articles in 2021`}
67+
</div>
68+
<div className="text-xl underline decoration-sky-500 decoration-2">
69+
{`You have written more articles in the month of ${articleProcessedData.monthHighArticles}`}
70+
</div>
71+
</div>
72+
73+
<h4 className="text-center py-4 underline decoration-cyan-300 text-xl mb-4">
74+
These are a few stats of your articles which you have written in 2021
75+
</h4>
76+
77+
<div className="flex flex-row justify-between">
78+
<StatsContainer title={`Views`} value={`${stats.totalViews}`} />
79+
<StatsContainer title={`Comments`} value={`${stats.totalComments}`} />
80+
<StatsContainer title={`Reactions`} value={`${stats.totalReactions}`} />
81+
<StatsContainer title={`Articles`} value={`${stats.totalArticles}`} />
82+
<StatsContainer
83+
title={`Reading minutes`}
84+
value={`${stats.totalReadingTime} min`}
85+
/>
86+
</div>
87+
88+
<div className="my-2 flex flex-col">
89+
<div className="my-4">
90+
<ChartProvider
91+
data={articleProcessedData.monthWiseGraphData}
92+
height={300}
93+
>
94+
<XAxis dataKey="month" />
95+
<BarSeries dataKey="articles" />
96+
<YAxis dataKey="articles" format={(count) => `${count}`} />
97+
<Tooltip>
98+
{({ month, articles }) => `Articles in ${month}: ${articles}`}
99+
</Tooltip>
100+
</ChartProvider>
101+
</div>
102+
</div>
103+
</div>
104+
);
105+
}
106+
interface StatsContainerProps {
107+
title: string;
108+
value: string;
109+
}
110+
const StatsContainer: React.FC<StatsContainerProps> = ({ title, value }) => {
111+
return (
112+
<div className="border-solid rounded-lg bg-lime-200 shadow-lime-200/50 p-4 w-1/6 flex flex-col text-center">
113+
<div className="my-2">{title}</div>
114+
<div className="font-bold underline decoration-pink-500 decoration-2">
115+
{value}
116+
</div>
117+
</div>
118+
);
119+
};

app/routes/index.tsx

+55-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,62 @@
1-
import { Outlet } from "remix";
1+
import {
2+
ActionFunction,
3+
Form,
4+
Outlet,
5+
redirect,
6+
useActionData,
7+
useTransition,
8+
} from "remix";
9+
import { cookie } from "~/utils/cookie.server";
10+
11+
type ActionData = {
12+
formError?: string;
13+
fieldErrors?: { apiKey: string | undefined };
14+
fields?: {
15+
apiKey: string;
16+
};
17+
};
18+
19+
export const action: ActionFunction = async ({ request }) => {
20+
const { apiKey } = Object.fromEntries(await request.formData());
21+
22+
if (typeof apiKey !== "string") {
23+
throw new Error(`Please check the API key and form`);
24+
}
25+
26+
const cookieHeader = await cookie.serialize({ apiKey });
27+
28+
return redirect("/app", {
29+
headers: {
30+
"Set-Cookie": cookieHeader,
31+
},
32+
});
33+
};
234

335
export default function Index() {
36+
const actionData = useActionData<ActionData>();
37+
const transition = useTransition();
38+
439
return (
5-
<div className="text-slate-200">
40+
<div className="center">
641
<h1 className="text-2xl text-sky-400">Welcome to Dev.to wrapped</h1>
42+
<Form method="post">
43+
<div>
44+
<label>
45+
API Key:{" "}
46+
<input
47+
className="form-input px-4 w-64 h-8 rounded-lg border-solid border-sky-500"
48+
type="text"
49+
name="apiKey"
50+
/>
51+
</label>
52+
</div>
53+
<button
54+
type="submit"
55+
className="button shadow-lg w-32 p-2 mt-4 text-white shadow-black/50 bg-black rounded-lg"
56+
>
57+
Add
58+
</button>
59+
</Form>
760
<Outlet />
861
</div>
962
);

app/routes/user/$userId.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const loader: LoaderFunction = async ({ request, params }) => {
2929
return articleProcessedData;
3030
};
3131

32-
export default function JokeRoute() {
32+
export default function AppRoute() {
3333
const articleProcessedData = useLoaderData<ArticleProcessedData>();
3434
const stats = articleProcessedData.stats;
3535

0 commit comments

Comments
 (0)