Skip to content

Commit

Permalink
Merge pull request #27 from IFRCGo/feature/source-feeds
Browse files Browse the repository at this point in the history
Add  View all source
  • Loading branch information
barshathakuri authored Apr 23, 2024
2 parents 42f28d2 + 74a9fce commit 22eb370
Show file tree
Hide file tree
Showing 11 changed files with 760 additions and 504 deletions.
8 changes: 3 additions & 5 deletions env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import { defineConfig, Schema } from '@julr/vite-plugin-validate-env';

// TODO: Integrate .env for CI and remove optional() call on required fields
export default defineConfig({
APP_ENVIRONMENT: Schema.string.optional(),
APP_GRAPHQL_API_ENDPOINT: Schema.string(),
APP_MAPBOX_ACCESS_TOKEN: Schema.string(),
APP_TITLE: Schema.string(),
APP_GRAPHQL_CODEGEN_ENDPOINT: Schema.string.optional(),
APP_TITLE: Schema.string.optional(),
APP_MAPBOX_ACCESS_TOKEN: Schema.string.optional(),
APP_GRAPHQL_ENDPOINT: Schema.string.optional(),
})
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"vite-plugin-compression2": "^1.0.0",
"vite-plugin-radar": "^0.9.2",
"vite-plugin-svgr": "^4.2.0",
"vite-plugin-webfont-dl": "^3.9.1",
"vite-plugin-webfont-dl": "3.9.2",
"vite-tsconfig-paths": "^4.2.2",
"vitest": "^1.1.0"
}
Expand Down
10 changes: 10 additions & 0 deletions src/App/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,22 @@ const alertDetails = myWrapRoute({
parent: root,
});

// TODO: rename this route and view name to "AllSources" or just "Sources"
const viewAllSource = myWrapRoute({
title: 'ViewAllSource',
path: 'sources',
component: () => import('#views/SourcesList'),
componentProps: {},
parent: root,
});

export const wrappedRoutes = {
root,
home,
preferences,
alertDetails,
resource,
viewAllSource,
};

export const unwrappedRoutes = unwrapRoute(Object.values(wrappedRoutes));
Expand Down
7 changes: 2 additions & 5 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,15 @@ import {
InMemoryCache,
} from '@apollo/client';

import { api } from '#config';

import App from './App';

const webappRootId = 'webapp-root';
const webappRootElement = document.getElementById(webappRootId);

const APP_GRAPHQL_ENDPOINT = 'http://localhost:8000/graphql/';
const client = new ApolloClient({
uri: api,
uri: APP_GRAPHQL_ENDPOINT,
cache: new InMemoryCache(),
});

if (!webappRootElement) {
// eslint-disable-next-line no-console
console.error(`Could not find html element with id '${webappRootId}'`);
Expand Down
3 changes: 2 additions & 1 deletion src/views/Home/AlertsView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
CountryListQuery,
CountryListQueryVariables,
} from '#generated/types/graphql';
import routes from '#routes';

import useAlertFilters from '../useAlertFilters';
import AlertsAside from './AlertsAside';
Expand Down Expand Up @@ -78,7 +79,7 @@ function AlertsView(props: Props) {
// TODO: Add sources link
<Link
className={styles.sources}
to="/"
to={routes.viewAllSource.absolutePath}
>
{strings.mapViewAllSources}
</Link>
Expand Down
6 changes: 6 additions & 0 deletions src/views/SourcesList/SourceCard/i18n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"namespace": "sourceCard",
"strings": {
"sourceCardAlt": "Logo"
}
}
52 changes: 52 additions & 0 deletions src/views/SourcesList/SourceCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Link } from 'react-router-dom';
import {
Container,
Header,
} from '@ifrc-go/ui';
import { useTranslation } from '@ifrc-go/ui/hooks';

import { SourceFeedsQuery } from '#generated/types/graphql';

import i18n from './i18n.json';
import styles from './styles.module.css';

type SourceFeed = NonNullable<NonNullable<SourceFeedsQuery['public']>['feeds']>['items'][number];

interface Props {
data: SourceFeed;
}

function SourceCard(props: Props) {
const {
data,
} = props;

const strings = useTranslation(i18n);

return (
<Link
className={styles.sourceCard}
to={data?.url}
>
<Container
childrenContainerClassName={styles.sourceDetail}
>
<img
className={styles.figure}
src={data?.languages?.map((image) => image.logo)?.[0] || ''}
alt={strings.sourceCardAlt}
/>
<div className={styles.title}>
<Header
heading={data?.languages?.map((lang) => lang.name)}
headingLevel={5}
/>
<div className={styles.language}>
{data?.languages?.map((name) => name.language)}
</div>
</div>
</Container>
</Link>
);
}
export default SourceCard;
39 changes: 39 additions & 0 deletions src/views/SourcesList/SourceCard/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.source-card {
display: flex;
gap: var(--go-ui-spacing-md);
transition: box-shadow var(--go-ui-duration-transition-slow) ease-in-out;
border-radius: var(--go-ui-border-radius-lg);
box-shadow: var(--go-ui-box-shadow-md);
text-decoration: none;

&:hover {
box-shadow: var(--go-ui-box-shadow-xl);
}

.source-detail {
display: flex;
gap: var(--go-ui-spacing-lg);
border-radius: var(--go-ui-border-radius-lg);
padding: var(--go-ui-spacing-lg);

.title {
display: flex;
flex-direction: column;
gap: var(--go-ui-spacing-md);
justify-content: space-between;
color: var(--go-ui-color-text);
}

.language {
width: fit-content;
text-decoration: none;
color: var(--go-ui-color-gray-60);
font-weight: var(--go-ui-font-weight-medium);
}

.figure {
width: 10rem;
height: 4rem;
}
}
}
6 changes: 6 additions & 0 deletions src/views/SourcesList/i18n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"namespace": "viewAllSource",
"strings": {
"sourceFeedsTitle":"Source Feeds"
}
}
120 changes: 120 additions & 0 deletions src/views/SourcesList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {
useCallback,
useMemo,
useState,
} from 'react';
import {
gql,
useQuery,
} from '@apollo/client';
import {
Container,
Pager,
RawList,
} from '@ifrc-go/ui';
import { useTranslation } from '@ifrc-go/ui/hooks';
import {
isDefined,
isNotDefined,
} from '@togglecorp/fujs';

import Page from '#components/Page';
import {
SourceFeedsQuery,
SourceFeedsQueryVariables,
} from '#generated/types/graphql';

import SourceCard from './SourceCard';

import i18n from './i18n.json';

const SOURCE_FEEDS = gql`
query SourceFeeds($pagination: OffsetPaginationInput) {
public {
feeds(pagination: $pagination) {
limit
offset
items {
languages {
logo
name
language
id
}
id
url
}
count
}
}
}
`;

type SourceFeed = NonNullable<NonNullable<SourceFeedsQuery['public']>['feeds']>['items'][number];

const MAX_ITEM_PER_PAGE = 21;

const keySelector = (source: SourceFeed) => source.id;

// eslint-disable-next-line import/prefer-default-export
export function Component() {
const strings = useTranslation(i18n);
const [activePage, setActivePage] = useState(1);

const variables = useMemo(() => ({
pagination: {
offset: (activePage - 1) * MAX_ITEM_PER_PAGE,
limit: MAX_ITEM_PER_PAGE,
},
}), [
activePage,
]);

const {
data: sourceFeedsResponse,
loading: sourceFeedsLoading,
error: sourceFeedsError,
} = useQuery<SourceFeedsQuery, SourceFeedsQueryVariables>(
SOURCE_FEEDS,
{ variables },
);

const rendererParams = useCallback((_: string, value: SourceFeed) => ({
data: value,
}), []);

return (
<Page
title="AlertHub - Sources"
heading={strings.sourceFeedsTitle}
>
<Container
footerActions={(
<Pager
activePage={activePage}
itemsCount={sourceFeedsResponse?.public?.feeds?.count ?? MAX_ITEM_PER_PAGE}
maxItemsPerPage={MAX_ITEM_PER_PAGE}
onActivePageChange={setActivePage}
/>
)}
contentViewType="grid"
numPreferredGridContentColumns={3}
pending={sourceFeedsLoading}
errored={isDefined(sourceFeedsError)}
errorMessage={sourceFeedsError?.message}
empty={isNotDefined(sourceFeedsResponse)
|| sourceFeedsResponse.public.feeds.items.length === 0}
spacing="comfortable"
>
<RawList
data={sourceFeedsResponse?.public.feeds.items}
renderer={SourceCard}
rendererParams={rendererParams}
keySelector={keySelector}
/>
</Container>
</Page>
);
}

Component.displayName = 'SourcesList';
Loading

0 comments on commit 22eb370

Please sign in to comment.