Skip to content

Commit

Permalink
CType moderation
Browse files Browse the repository at this point in the history
  • Loading branch information
lukeg90 committed Aug 4, 2023
1 parent 31230fd commit cf6d55c
Show file tree
Hide file tree
Showing 18 changed files with 323 additions and 14 deletions.
4 changes: 4 additions & 0 deletions src/components/CTypeDetails/CTypeDetails.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ describe('CTypeDetails', () => {
const html = await getSnapshotHtmlForPath('test/CTypeDetails-everything');
expect(html).toMatchSnapshot();
});
it('should handle hidden CType', async () => {
const html = await getSnapshotHtmlForPath('test/CTypeDetails-hidden');
expect(html).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`CTypeDetails > should handle hidden CType 1`] = `
<body>
<h1>Page not found</h1>
</body>
`;

exports[`CTypeDetails > should handle kitchen sink CType 1`] = `
<section class="_container">
<h1 class="title">Everything everywhere</h1>
Expand Down
103 changes: 103 additions & 0 deletions src/components/Moderation/Moderation.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
.table {
width: 100%;
border-collapse: collapse;
}

.grid {
display: grid;
grid-template-columns: 8rem 1fr 6rem;
}

.tableRow {
display: contents;
}

.tableRow:nth-child(2n) {
background-color: rgb(41 135 205 / 10%);
}

.tableRow:nth-child(2n + 1) {
background-color: rgb(41 135 205 / 30%);
}

.tableCell {
padding: 0.5rem;
border: 2px solid rgba(var(--color-black-rgb));
min-width: 0;
overflow-wrap: anywhere;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 0.5rem;
}

.tableHeader {
composes: grid;
background-color: var(--color-link);
}

.tableBody {
composes: grid;
}

.tableHeaderDate {
composes: tableCell;
grid-column: 1;
}

.tableHeaderDetails {
composes: tableCell;
grid-column: 2;
}

.tableHeaderHidden {
composes: tableCell;
grid-column: 3;
}

.tableDataDate {
composes: tableCell;
grid-column: 1;
justify-content: flex-start;
}

.tableDataDetails {
composes: tableCell;
grid-column: 2;
justify-content: flex-start;
}

.tableDataHidden {
composes: tableCell;
grid-column: 3;
justify-content: flex-start;
align-items: center;
}

.tableDataDetails p {
margin: 0;
}

.hidden {
color: rgb(176 213 251 / 40%);
}

.iconButton {
border: 0;
padding: 0;
inline-size: 3rem;
block-size: 3rem;
display: inline-flex;
cursor: pointer;
}

.hide {
composes: iconButton;
background: url('./eye-solid.svg') no-repeat center/auto 0.75rem;
}

.unhide {
composes: iconButton;
background: url('./eye-slash-solid.svg') no-repeat center/auto 0.75rem;
}
105 changes: 103 additions & 2 deletions src/components/Moderation/Moderation.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,95 @@
import type { CTypeData } from '../../models/ctype';

import { MouseEvent, useCallback, useEffect, useState } from 'react';

import styles from './Moderation.module.css';
import buttonStyles from '../Button.module.css';
import containerStyles from '../Container.module.css';

import { paths } from '../../paths';
import { sessionHeader } from '../../utilities/sessionHeader';
import { exceptionToError } from '../../utilities/exceptionToError';

import { apiWindow, getCompatibleExtensions, getSession } from './session';

export function Moderation() {
function CType({ sessionId, cType }: { sessionId: string; cType: CTypeData }) {
const {
id,
createdAt,
title,
properties,
description,
tags,
isHidden: initialIsHidden,
} = cType;

const [isHidden, setIsHidden] = useState(initialIsHidden);
const [processing, setProcessing] = useState(false);

const toggleHidden = useCallback(async () => {
try {
setProcessing(true);
const headers = {
'Content-Type': 'application/json',
[sessionHeader]: sessionId,
};
const response = await fetch(paths.moderationCType, {
method: 'POST',
headers,
body: JSON.stringify({ id, isHidden: !isHidden }),
});

if (!response.ok) {
throw new Error('Unable to moderate CType');
}
setIsHidden(!isHidden);
} catch (exception) {
console.error(exception);
} finally {
setProcessing(false);
}
}, [id, isHidden, sessionId]);

const propertyNames = Object.keys(properties).join(', ');
const tagNames = tags?.map((tag) => `#${tag}`).join(', ');

return (
<tr className={styles.tableRow}>
<td
className={`${styles.tableDataDate} ${isHidden ? styles.hidden : ''}`}
>
{createdAt.toLocaleString()}
</td>
<td className={styles.tableDataDetails}>
<p className={isHidden ? styles.hidden : ''}>{title}</p>
{description && (
<p className={isHidden ? styles.hidden : ''}>{description}</p>
)}
{propertyNames && (
<p className={isHidden ? styles.hidden : ''}>{propertyNames}</p>
)}
{tagNames && (
<p className={isHidden ? styles.hidden : ''}>{tagNames}</p>
)}
</td>
<td className={styles.tableDataHidden}>
<button
type="button"
onClick={toggleHidden}
disabled={processing}
className={isHidden ? styles.unhide : styles.hide}
aria-label={isHidden ? 'Unhide CType' : 'Hide CType'}
/>
</td>
</tr>
);
}

interface Props {
cTypes: CTypeData[];
}

export function Moderation({ cTypes }: Props) {
const { kilt } = apiWindow;

const [sessionId, setSessionId] = useState<string>();
Expand Down Expand Up @@ -84,6 +167,7 @@ export function Moderation() {
onClick={handleClick}
value={extension}
type="button"
className={buttonStyles.primary}
>
Sign in with {kilt[extension].name}
</button>
Expand All @@ -94,5 +178,22 @@ export function Moderation() {
);
}

return <div></div>;
return (
<div className={containerStyles.container}>
<table className={styles.table}>
<thead className={styles.tableHeader}>
<tr className={styles.tableRow}>
<th className={styles.tableHeaderDate}>Creation Date</th>
<th className={styles.tableHeaderDetails}>Details</th>
<th className={styles.tableHeaderHidden}>Hidden</th>
</tr>
</thead>
<tbody className={styles.tableBody}>
{cTypes.map((cType) => (
<CType key={cType.id} sessionId={sessionId} cType={cType} />
))}
</tbody>
</table>
</div>
);
}
1 change: 1 addition & 0 deletions src/components/Moderation/eye-slash-solid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/components/Moderation/eye-solid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 1 addition & 5 deletions src/components/index/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`index.astro > should render all CTypes 1`] = `
exports[`index.astro > should render all CTypes except hidden ones 1`] = `
<body>
<header>
<span></span>
Expand Down Expand Up @@ -50,8 +50,6 @@ exports[`index.astro > should render search results 1`] = `
<input type="search" name="query" value="nested" placeholder="Search query" aria-label="Search query" class="_search1 _component" /><button type="submit" class="_search6 _primary">Search</button>
</form>
<p>No tag results for “#nested”</p>
<p>CType results for “nested”:</p>
<ul class="cTypeResults">
<li class="_container">
Expand Down Expand Up @@ -127,8 +125,6 @@ exports[`index.astro > should render when no search results 1`] = `
<input type="search" name="query" value="asdagalasdg" placeholder="Search query" aria-label="Search query" class="_search1 _component" /><button type="submit" class="_search6 _primary">Search</button>
</form>
<p>No tag results for “#asdagalasdg”</p>
<p>No CType results for “asdagalasdg”</p>
</search>
</body>
Expand Down
8 changes: 6 additions & 2 deletions src/components/index/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ import { Tag } from '../../models/tag';

beforeEach(async () => {
await resetDatabase();
await CTypeModel.bulkCreate([mockCTypes.example, mockCTypes.nestedProperty]);
await CTypeModel.bulkCreate([
mockCTypes.example,
mockCTypes.nestedProperty,
mockCTypes.hidden,
]);
await Tag.create({ tagName: 'example', cTypeId: mockCTypes.example.id });
await AttestationModel.bulkCreate(mockAttestations);
});

describe('index.astro', () => {
it('should render all CTypes', async () => {
it('should render all CTypes except hidden ones', async () => {
const html = await getSnapshotHtmlForPath('');
expect(html).toMatchSnapshot();
});
Expand Down
16 changes: 16 additions & 0 deletions src/migrations/20230802123923-isHidden.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn('CTypes', 'isHidden', {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false,
});
},

async down(queryInterface) {
await queryInterface.removeColumn('CTypes', 'isHidden');
},
};
6 changes: 6 additions & 0 deletions src/models/ctype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface CTypeDataInput extends Omit<ICType, '$id' | '$schema'> {

export interface CTypeData extends CTypeDataInput {
attestationsCount: string;
isHidden: boolean;
tags?: Pick<Tag, 'dataValues'>[];
}

Expand Down Expand Up @@ -61,6 +62,11 @@ export const CTypeModelDefinition: ModelAttributes = {
description: {
type: DataTypes.STRING,
},
isHidden: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
},
};

const fields = Object.keys(CTypeModelDefinition)
Expand Down
2 changes: 1 addition & 1 deletion src/pages/ctype/[id].astro
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const success = Astro.url.searchParams.has('success');
const cType = await CType.scope('stats').findByPk(id, { include: 'tags' });
if (!cType) {
if (!cType || cType.dataValues.isHidden) {
return new NotFoundResponse();
}
---
Expand Down
2 changes: 1 addition & 1 deletion src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const tagResults = await Tag.findAll({
const where = {
block: { [Op.ne]: null },
isHidden: false,
...(query && {
search: { [Op.match]: fn('websearch_to_tsquery', 'english', query) },
}),
Expand All @@ -45,7 +46,6 @@ const {
<search class={containerStyles.search}>
<SearchForm action="/" query={query} />

{query && tagResults.length === 0 && <p>No tag results for “#{query}”</p>}
{
tagResults.length > 0 && (
<Fragment>
Expand Down
Loading

0 comments on commit cf6d55c

Please sign in to comment.