refactor(dashboard): show current user projects on dashboard@@ -1,5 +1,5 @@
{
- "_generatedAtUnix": 1663773368816,
+ "_generatedAtUnix": 1663788290484,
"_hashAlgorithm": "sha1",
"_version": 2,
"islands": {
@@ -9,6 +9,12 @@
"pathBundle": "./public/.islands/InstantRouterIndicator.bundle.js",
"pathSourceMap": "./public/.islands/InstantRouterIndicator.bundle.js.map"
},
+ "RepositoriesList": {
+ "hash": "d0e1a580d6857fbf98a08bf5a6242ec5902dac9b",
+ "pathSource": "./app/islands/RepositoriesList.tsx",
+ "pathBundle": "./public/.islands/RepositoriesList.bundle.js",
+ "pathSourceMap": "./public/.islands/RepositoriesList.bundle.js.map"
+ },
"RepositoryCreateForm": {
"hash": "13bc29f8548d0df057f56a78bb6f2f6b31cb7d31",
"pathSource": "./app/islands/RepositoryCreateForm.tsx",
@@ -30,7 +36,7 @@
},
"views": {
"HomeView": {
- "hash": "f1f68abb235c063ad79a1921fc4be2e8f8b2597a",
+ "hash": "3e321a2058675d89de2582ff3758f3287ac69a01",
"pathSource": "./app/views/HomeView.tsx"
},
"InternalErrorView": {
@@ -38,7 +44,7 @@
"pathSource": "./app/views/InternalErrorView.tsx"
},
"DashboardView": {
- "hash": "9b287b43216a2a41bb961737801dfd950945b6af",
+ "hash": "316530648e98734fd1e983c8dcf9062d772363fb",
"pathSource": "./app/views/auth/DashboardView.tsx"
},
"LoginView": {
@@ -66,7 +72,7 @@
"pathSource": "./app/views/repository/RepositoryDetailsView.tsx"
},
"RepositoryExploreView": {
- "hash": "06685120a2cdbcb6671ac0436054a3b1345594ef",
+ "hash": "416d8fe270478274dc12f6ac09e75bb789842ec3",
"pathSource": "./app/views/repository/RepositoryExploreView.tsx"
}
}
@@ -17,10 +17,12 @@ const getDashboardView: ReqHandler = async (request, reply) => {
const usersService = makeUsersService({ request });
const currentUser = await usersService.getUserById(curr_user_uid);
+ const repositories = await usersService.getUserRepositories(curr_user_uid);
const reqHandler = reply.makeRequestHandler(request, reply);
return reqHandler<DashboardViewProps>(DashboardView.name, {
currentUser,
+ repositories,
});
};
@@ -1,6 +1,6 @@
// 1st-party
import type { ReqHandler } from "@ethicdevs/react-monolith";
-// app
+// app services
import { makeRepositoryService } from "../../services/repository";
// app views
import RepositoryExploreView, {
@@ -0,0 +1,40 @@
+// 1st-party
+import type { ReactIsland } from "@ethicdevs/react-monolith";
+// 3rd-party
+import React from "react";
+// generated via script[generate:prisma]
+import { Organization, Repository } from "@prisma/client";
+
+export interface RepositoriesListProps {
+ repositories: Array<Repository & { parentOrg: Organization }>;
+}
+
+const RepositoriesList: ReactIsland<RepositoriesListProps> = ({
+ repositories,
+}) => {
+ return (
+ <>
+ {repositories.map((repo) => (
+ <div key={repo.id}>
+ <h1>
+ <a href={`/${repo.parentOrg.slug}/${repo.slug}`}>
+ {repo.parentOrg.displayName || repo.parentOrg.slug}
+ {" / "}
+ {repo.displayName || repo.slug}
+ {" ∙ "}
+ <span style={{ textTransform: "capitalize" }}>
+ ({repo.visibility.toLowerCase()})
+ </span>
+ </a>
+ </h1>
+ <div>
+ <p>{repo.shortDescription}</p>
+ </div>
+ </div>
+ ))}
+ </>
+ );
+};
+
+RepositoriesList.displayName = "RepositoriesList";
+export default RepositoriesList;
@@ -23,13 +23,13 @@ const makeGetLinguistFileInfos: ServiceMethodFactory<
};
}
- const language =
+ let language =
content != null
? deps.languageDetect.contents(path, content)
: deps.languageDetect.filename(path);
if (language == null || language in deps.languagesMap === false) {
- throw new Error(`Invalid language: null.`);
+ language = "Shell";
}
const languageInfos = deps.languagesMap[language];
@@ -0,0 +1,45 @@
+// 1st-party
+import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
+// generated via script[generate:prisma]
+import { Organization, Repository } from "@prisma/client";
+// app
+import type { UsersServiceDeps } from "./types";
+
+const getUserRepositories: ServiceMethodFactory<
+ UsersServiceDeps,
+ [string],
+ Promise<(Repository & { parentOrg: Organization })[]>
+> = ({ request }) => {
+ return async (userId) => {
+ const userRepos = await request.prisma.repository.findMany({
+ include: {
+ organization: true,
+ },
+ where: {
+ OR: [
+ {
+ organization: {
+ ownerId: userId,
+ },
+ },
+ {
+ organization: {
+ memberships: {
+ some: {
+ userId,
+ },
+ },
+ },
+ },
+ ],
+ },
+ });
+
+ return userRepos.map(({ organization: parentOrg, ...repo }) => ({
+ ...repo,
+ parentOrg,
+ }));
+ };
+};
+
+export default getUserRepositories;
@@ -8,6 +8,7 @@ import { default as makeGetUserById } from "./getUserById";
import { default as makeGetUserByUsername } from "./getUserByUsername";
import { default as makeGetUserOrganizationMemberships } from "./getUserOrganizationMemberships";
import { default as makeGetUserOrganizations } from "./getUserOrganizations";
+import { default as makeGetUserRepositories } from "./getUserRepositories";
export const makeUsersService = makeService<UsersServiceAPI, UsersServiceDeps>({
getUserByEmailAddress: makeGetUserByEmailAddress,
@@ -15,4 +16,5 @@ export const makeUsersService = makeService<UsersServiceAPI, UsersServiceDeps>({
getUserByUsername: makeGetUserByUsername,
getUserOrganizationMemberships: makeGetUserOrganizationMemberships,
getUserOrganizations: makeGetUserOrganizations,
+ getUserRepositories: makeGetUserRepositories,
});
@@ -3,8 +3,12 @@ import { ServiceApiContract } from "@ethicdevs/react-monolith";
// 3rd-party
import { FastifyRequest } from "fastify";
// generated via script[generate:prisma]
-import { Organization, OrganizationMembership, User } from "@prisma/client";
-// app
+import {
+ Organization,
+ OrganizationMembership,
+ Repository,
+ User,
+} from "@prisma/client";
export interface UsersServiceAPI extends ServiceApiContract {
getUserById(userId: string): Promise<User | null>;
@@ -14,6 +18,9 @@ export interface UsersServiceAPI extends ServiceApiContract {
userId: string
): Promise<OrganizationMembership[]>;
getUserOrganizations(userId: string): Promise<Organization[]>;
+ getUserRepositories(
+ userId: string
+ ): Promise<(Repository & { parentOrg: Organization })[]>;
}
export interface UsersServiceDeps {
@@ -17,8 +17,10 @@ const HomeView: ReactView<HomeViewProps> = (props) => {
<Layout {...commonProps}>
<PageWrapper>
<StyledButtonsRow>
- <ButtonAnchor href={"/auth/register"}>Get Started</ButtonAnchor>
- <ButtonAnchor href={"/features"}>Learn More</ButtonAnchor>
+ <ButtonAnchor href={"/auth/register"}>Create an Account</ButtonAnchor>
+ <ButtonAnchor href={"/repo/explore"}>
+ Explore Repositories
+ </ButtonAnchor>
</StyledButtonsRow>
</PageWrapper>
</Layout>
@@ -28,11 +30,10 @@ const HomeView: ReactView<HomeViewProps> = (props) => {
const StyledButtonsRow = styled.div`
display: flex;
flex-flow: row wrap;
- align-items: stretch;
+ align-items: center;
justify-content: center;
gap: 16px;
margin-top: 24px;
- max-width: 440px;
width: 100%;
`;
@@ -3,20 +3,34 @@ import type { ReactView } from "@ethicdevs/react-monolith";
// 3rd-party
import React from "react";
// generated via script[generate:prisma]
-import type { User } from "@prisma/client";
+import type { Organization, Repository, User } from "@prisma/client";
// app
import type { CommonProps } from "../../types";
import { Layout, PageWrapper } from "../../components";
+// app islands
+import RepositoriesList from "../../islands/RepositoriesList";
export interface DashboardViewProps extends CommonProps {
currentUser: User;
+ repositories: (Repository & { parentOrg: Organization })[];
}
-const DashboardView: ReactView<DashboardViewProps> = ({ commonProps }) => {
+const DashboardView: ReactView<DashboardViewProps> = ({
+ commonProps,
+ currentUser,
+ repositories,
+}) => {
return (
<Layout {...commonProps}>
<PageWrapper>
- <h1>Hey, welcome!</h1>
+ <h1>Hey {currentUser.displayName || currentUser.username}, welcome!</h1>
+ <h2>Repositories you own and/or contribute to</h2>
+ <div
+ data-islandid={`${RepositoriesList.name}$$0`}
+ style={{ width: "100%" }}
+ >
+ <RepositoriesList repositories={repositories} />
+ </div>
</PageWrapper>
</Layout>
);
@@ -7,6 +7,8 @@ import type { Organization, Repository } from "@prisma/client";
// app
import type { CommonProps } from "../../types";
import { Layout, PageWrapper } from "../../components";
+// app islands
+import RepositoriesList from "../../islands/RepositoriesList";
export interface RepositoryExploreViewProps extends CommonProps {
repositories: (Repository & { parentOrg: Organization })[];
@@ -19,24 +21,14 @@ const RepositoryExploreView: ReactView<RepositoryExploreViewProps> = ({
return (
<Layout {...commonProps}>
<PageWrapper>
- {repositories.map((repo) => (
- <div key={repo.id}>
- <h1>
- <a href={`/${repo.parentOrg.slug}/${repo.slug}`}>
- {repo.parentOrg.displayName || repo.parentOrg.slug}
- {" / "}
- {repo.displayName || repo.slug}
- {" ∙ "}
- <span style={{ textTransform: "capitalize" }}>
- ({repo.visibility.toLowerCase()})
- </span>
- </a>
- </h1>
- <div>
- <p>{repo.shortDescription}</p>
- </div>
- </div>
- ))}
+ <h1>Explore public repositories</h1>
+ <h2>Discover your next project to contribute to!</h2>
+ <div
+ data-islandid={`${RepositoriesList.name}$$0`}
+ style={{ width: "100%" }}
+ >
+ <RepositoriesList repositories={repositories} />
+ </div>
</PageWrapper>
</Layout>
);
@@ -17,7 +17,7 @@
"resolveJsonModule": true,
"rootDir": ".",
"sourceMap": true,
- "target": "es5",
+ "target": "es2015",
// Strict Type-Checking Options
"strict": true,