Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ yarn-error.log*
.env.development.local
.env.test.local
.env.production.local

.env.cloud
# vercel
.vercel

Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Contributions for the following are very welcome.
- [ ] Link Suggestion on 404
- [x] Link Usage Metrics
- [x] Number: Usage Total Count
- [ ] Graph: Usage over Time
- [x] Graph: Usage of last 14 days
- [ ] Link Ownership
- [ ] Link Parameters
- [ ] Private Links
Expand Down Expand Up @@ -196,6 +196,8 @@ AUTH0_COOKIE_SECRET=<auth0-cookie-secret>
AUTH0_COOKIE_DOMAIN=localhost
AUTH0_REDIRECT_URL=http://localhost:3000/api/callback
AUTH0_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
HOSTNAME=http://localhost:3000
LOGONAME=go.mydomain.dev
EOL

docker-compose up
Expand All @@ -219,6 +221,8 @@ AUTH0_COOKIE_SECRET=<auth0-cookie-secret>
AUTH0_COOKIE_DOMAIN=localhost
AUTH0_REDIRECT_URL=http://localhost:3000/api/callback
AUTH0_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
HOSTNAME=http://localhost:3000
LOGONAME=go.mydomain.dev
EOL

# Environment Variables for the Cloud SQL Proxy
Expand Down
181 changes: 114 additions & 67 deletions components/LinkTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ import {
DropdownMenu,
Icon,
} from 'bumbag';

import { GetAllLinksQuery } from '../../lib/queries/getAllLinks.graphql';
import { Sparklines, SparklinesLine } from 'react-sparklines';
import { compareAsc, eachDayOfInterval, sub } from 'date-fns';
import { formatWithOptions } from 'date-fns/fp';
import { nl } from 'date-fns/locale';
import {
GetAllLinksQuery,
LinkUsageMetric,
} from '../../lib/queries/getAllLinks.graphql';

interface Props {
data: GetAllLinksQuery;
Expand All @@ -20,6 +26,34 @@ interface Props {
onEdit: (linkId: string) => void | Promise<void>;
}

const dateToString = formatWithOptions({ locale: nl }, 'dd/MM/yyyy');

const convertMetricsToLineChartData = (
linkUsageMetrics: Pick<LinkUsageMetric, 'accessedAt'>[]
) => {
const days = eachDayOfInterval({
end: new Date(),
start: sub(new Date(), {
days: 14,
}),
});

const dates = [
...linkUsageMetrics.map((metric) => new Date(metric.accessedAt)),
...days,
].sort(compareAsc);

const countPerDate = dates.map(dateToString).reduce(
(acc, date) => ({
...acc,
[date]: (acc[date] || 0) + 1,
}),
{} as Record<string, number>
);

return Object.values(countPerDate);
};

export const LinkTable: React.FC<Props> = ({
data,
isDeleteEnabled,
Expand All @@ -45,76 +79,89 @@ export const LinkTable: React.FC<Props> = ({
<Table.Row>
<Table.HeadCell>Alias</Table.HeadCell>
<Table.HeadCell>Destination</Table.HeadCell>
<Table.HeadCell textAlign="right">Usage</Table.HeadCell>
<Table.HeadCell textAlign="right"></Table.HeadCell>
<Table.HeadCell textAlign="center">
Usage <Text use="sub">(14 days)</Text>
</Table.HeadCell>
<Table.HeadCell textAlign="right">Actions</Table.HeadCell>
</Table.Row>
</Table.Head>
<Table.Body>
<>
{links?.nodes.map((link) => (
<>
<Table.Row key={link.id}>
<Table.Cell>{link.alias}</Table.Cell>
<Table.Cell>
<FannyLink
href={link.url}
style={{
display: 'block',
maxWidth: '350px',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}>
{link.url}
</FannyLink>
</Table.Cell>
<Table.Cell textAlign="right">
{link.linkUsageMetrics.totalCount}
</Table.Cell>
<Table.Cell textAlign="right">
<DropdownMenu
menu={
<>
<DropdownMenu.Item
disabled={!isEditEnabled}
iconBefore="solid-edit"
onClick={() => onEdit(link.id)}>
Edit
</DropdownMenu.Item>
<DropdownMenu.Item
iconBefore="solid-share"
onClick={() => {
const url = new URL(
link.alias,
document.location.origin
).toString();
onShare(url);
}}>
Share
</DropdownMenu.Item>
<DropdownMenu.Item
iconBefore="solid-chart-bar"
onClick={() => onAnalytics(link.id)}>
Analytics
</DropdownMenu.Item>
<DropdownMenu.Item
disabled={!isDeleteEnabled}
iconBefore="solid-trash-alt"
color="danger"
onClick={() => {
onDelete(link.id);
}}>
Delete
</DropdownMenu.Item>
</>
}>
<Button size="small">
<Icon icon="solid-edit" />
</Button>
</DropdownMenu>
</Table.Cell>
</Table.Row>
</>
<Table.Row key={link.id}>
<Table.Cell>{link.alias}</Table.Cell>
<Table.Cell>
<FannyLink
href={link.url}
style={{
display: 'block',
maxWidth: '350px',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}>
{link.url}
</FannyLink>
</Table.Cell>
<Table.Cell textAlign="center">
<Sparklines
data={convertMetricsToLineChartData(
link.linkUsageMetrics.nodes
)}
limit={14}
style={{ minWidth: '120px', maxWidth: '170px' }}>
<SparklinesLine
style={{ stroke: '#574feb', fill: '#eeedfd' }}
/>
</Sparklines>
<br />
<Text use="sub">
Total Usage: {link.linkUsageMetrics.totalCount}
</Text>
</Table.Cell>
<Table.Cell textAlign="right">
<DropdownMenu
menu={
<>
<DropdownMenu.Item
disabled={!isEditEnabled}
iconBefore="solid-edit"
onClick={() => onEdit(link.id)}>
Edit
</DropdownMenu.Item>
<DropdownMenu.Item
iconBefore="solid-share"
onClick={() => {
const url = new URL(
link.alias,
document.location.origin
).toString();
onShare(url);
}}>
Share
</DropdownMenu.Item>
<DropdownMenu.Item
iconBefore="solid-chart-bar"
onClick={() => onAnalytics(link.id)}>
Analytics
</DropdownMenu.Item>
<DropdownMenu.Item
disabled={!isDeleteEnabled}
iconBefore="solid-trash-alt"
color="danger"
onClick={() => {
onDelete(link.id);
}}>
Delete
</DropdownMenu.Item>
</>
}>
<Button size="small">
<Icon icon="solid-edit" />
</Button>
</DropdownMenu>
</Table.Cell>
</Table.Row>
))}
</>
</Table.Body>
Expand Down
3 changes: 3 additions & 0 deletions lib/queries/getAllLinks.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ query getAllLinks {
alias
linkUsageMetrics {
totalCount
nodes {
accessedAt
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"apollo-utilities": "^1.3.4",
"bumbag": "1.0.0-rc.11",
"bumbag-server": "1.0.0-rc.11",
"date-fns": "^2.16.1",
"emotion": "^10.0.27",
"express": "^4.17.1",
"express-jwt": "^6.0.0",
Expand All @@ -48,6 +49,7 @@
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-kawaii": "^0.16.0",
"react-sparklines": "^1.7.0",
"yup": "^0.29.1"
},
"devDependencies": {
Expand All @@ -60,6 +62,7 @@
"@types/jwt-decode": "^2.2.1",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.7",
"@types/react-sparklines": "^1.7.0",
"@types/yup": "^0.29.3",
"graphql-let": "0.x",
"prettier": "^2.0.5",
Expand Down
14 changes: 12 additions & 2 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,23 @@ import {
faShare,
faTrashAlt,
faChartBar,
faSignOutAlt,
} from '@fortawesome/free-solid-svg-icons';

const theme: ThemeConfig = {
modes: {
useSystemColorMode: true,
},
Icon: {
iconSets: [
{
icons: [faEdit, faShare, faTrashAlt, faChartBar],
icons: [
faEdit,
faShare,
faTrashAlt,
faChartBar,
faSignOutAlt,
],
prefix: 'solid-',
type: 'font-awesome',
},
Expand All @@ -30,7 +40,7 @@ export default function App({ Component, pageProps }: AppProps) {

return (
<ApolloProvider client={apolloClient}>
<ThemeProvider theme={theme}>
<ThemeProvider colorMode="dark" theme={theme}>
<ToastManager />
<Component {...pageProps} />
</ThemeProvider>
Expand Down
Loading