Skip to content

Commit

Permalink
Added add repository form (#14)
Browse files Browse the repository at this point in the history
* Added add repository form
  • Loading branch information
marlonbaeten authored Nov 7, 2022
1 parent e6f5d67 commit 9244add
Show file tree
Hide file tree
Showing 25 changed files with 294 additions and 71 deletions.
1 change: 0 additions & 1 deletion src/components/Cas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,3 @@ export default function Cas() {
</Layout>
);
}

6 changes: 6 additions & 0 deletions src/components/CasHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function CasHeader() {
const store = useStore();
const t = useTranslations();
const navigate = useNavigation();
const noParents = !store.ca || store.parents[store.ca].length === 0;

return (
<>
Expand All @@ -27,6 +28,11 @@ export default function CasHeader() {
/>
</div>
</div>
{noParents && (
<div className="notification error">
{t.caDetails.onboardingWarning}
</div>
)}
<div>
<ul className="tabs">
<li>
Expand Down
19 changes: 16 additions & 3 deletions src/components/CasRepository.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ import useTranslations from '../hooks/useTranslations';
import CasHeader from './CasHeader';
import RepoTable from './tables/RepoTable';
import Layout from './Layout';
import useNavigation from '../hooks/useNavigation';
import RepoModal from './forms/RepoModal';
import { useRoute } from 'react-router5';

export default function CasRepository() {
const t = useTranslations();
const store = useStore() as Store;

const navigate = useNavigation();
const { route } = useRoute();

const syncRepo = () => {
store.loadRepoStatus(true);
};
Expand All @@ -18,19 +23,27 @@ export default function CasRepository() {
return null;
}

const isset = store.repoStatus && store.ca && store.repoStatus[store.ca]?.last_exchange;

return (
<Layout>
{route.name === 'cas.repository.add' && (
<RepoModal />
)}
<CasHeader />
{store.repoStatus && store.ca && store.repoStatus[store.ca] && (
{isset ? (
<RepoTable
repo={store.repoStatus[store.ca]}
locale={store.locale}
/>
) : (
<button className="button" onClick={() => navigate({}, 'cas.repository.add')}>
{t.caDetails.repoTab.addRepo}
</button>
)}
<button className="button inverted" onClick={syncRepo}>
{t.caDetails.syncRepo}
</button>
</Layout>
);
}

2 changes: 1 addition & 1 deletion src/components/NotificationPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface NotificationProps {

export default function NotificationPopup({ notification, onClose }: NotificationProps) {
return (
<div className={`notification ${ notification.type }`}>
<div className={`notification popup ${ notification.type }`}>
<button className="close" onClick={onClose}>
&times;
</button>
Expand Down
67 changes: 67 additions & 0 deletions src/components/forms/RepoModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from 'react';
import useTranslations from '../../hooks/useTranslations';
import Modal from './Modal';
import clipboard from '../../img/clipboard.svg?url';
import download from '../../img/download.svg?url';
import upload from '../../img/upload.svg?url';
import NotificationPopup from '../NotificationPopup';
import useRepositoryActions from '../../hooks/useRepositoryActions';

export default function RepoModal() {
const t = useTranslations();
const {
notification,
setNotification,
request,
dataUrl,
setRequest,
response,
setResponse,
onSubmit,
onClose,
onCopy,
handleUpload,
} = useRepositoryActions();

return (
<Modal onClose={onClose}>
{notification && (
<NotificationPopup
notification={notification}
onClose={() => setNotification(null)}
/>
)}
<h3>{t.caDetails.repoTab.addRepo}</h3>
<form onSubmit={ onSubmit } method="POST">
<div>
<label>{t.caDetails.repoTab.request}</label>
<textarea name="request" readOnly value={request} id="request" onChange={(e) => setRequest(e.target.value)} />
<div>
<button className="button large icon" type="button" title={t.common.copy} onClick={onCopy}>
<img src={clipboard} alt={t.common.copy} />
</button>
<a className="button large icon" title={t.common.download} href={dataUrl} download="publisher_request.xml">
<img src={download} alt={t.common.download} />
</a>
</div>
</div>
<div>
<label>{t.caDetails.repoTab.response}</label>
<textarea name="response" value={response} onChange={(e) => setResponse(e.target.value)}/>
<input type="file" id="upload" onChange={handleUpload} />
<label className="button large icon" htmlFor="upload" title={t.common.dropOrClick}>
<img src={upload} alt={t.common.dropOrClick} />
</label>
</div>
<div className="actions">
<button className="button outline" onClick={onClose}>
{t.common.cancel}
</button>
<button type="submit" className="button">
{t.common.confirm}
</button>
</div>
</form>
</Modal>
);
}
17 changes: 12 additions & 5 deletions src/core/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default class Api {
if (response.headers.get('Content-Type') !== 'application/json'){
return await response.text() as ResponseType;
}

const json = await response.json();

if (response.status === 200) {
Expand Down Expand Up @@ -108,11 +109,11 @@ export default class Api {
}

getChildRequest(ca: string): Promise<string> {
return fetch(`/api/v1/cas/${ca}/id/child_request.xml`, {
headers: {
'Authorization': `Bearer ${this.token}`,
}})
.then((response) => response.text());
return this.get<string>(`/api/v1/cas/${ca}/id/child_request.xml`);
}

getPublisherRequest(ca: string): Promise<string> {
return this.get<string>(`/api/v1/cas/${ca}/id/publisher_request.xml`);
}

postParent(ca: string, name: string, text: string) {
Expand All @@ -121,6 +122,12 @@ export default class Api {
});
}

postRepository(ca: string, name: string, text: string) {
return this.post(`/api/v1/cas/${ca}/repo`, {
body: text
});
}

updateRoutes(ca: string, data: { added: Route[], removed: Route[] }) {
return this.get(`/api/v1/cas/${ca}/routes`, {
method: 'POST',
Expand Down
7 changes: 7 additions & 0 deletions src/core/handlers/handleCaData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ export default async function handleCaData(toState: State, store: Store) {
}
}

// add repo
if (toState.name === 'cas.repository.add' && toState.params.response) {
if (await store.addRepository(toState.params as ParentParams)) {
return Promise.reject({ redirect: {name: 'cas.repository', params: { ca: store.ca }} });
}
}

// load a list of available ca's
await store.loadCas();

Expand Down
12 changes: 12 additions & 0 deletions src/core/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,18 @@ export default class Store implements Data {
});
}

async addRepository(params: ParentParams) {
if (this.ca === null) {
return;
}

return await this.handleError(async () => {
await this.api.postRepository(this.ca as string, params.name, params.response || '');
await this.loadRepoStatus(true);
return true;
});
}

// update notification
setNotification(notification: Notification | null) {
this.notification = notification;
Expand Down
1 change: 1 addition & 0 deletions src/core/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export interface Translations {
repoTab: {
request: string,
response: string,
addRepo: string,
addRepoSuccess: string,
},
analyseThis: string,
Expand Down
19 changes: 11 additions & 8 deletions src/css/notification.css
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
.notification {
position: fixed;
z-index: 1000;
background: white;
top: 1rem;
right: 1rem;
box-shadow: 0 0.5rem 1rem 0 rgb(0 0 0 / 40%);
padding: 1.5rem 3.5rem;
background-repeat: no-repeat;
background-position: center left 1rem;
background-size: 1.5rem;
max-width: 30rem;
min-width: 20rem;
border-radius: var(--radius);
display: flex;
align-items: center;
}

.notification.popup {
position: fixed;
z-index: 1000;
top: 1rem;
right: 1rem;
max-width: 30rem;
min-width: 20rem;
box-shadow: 0 0.5rem 1rem 0 rgb(0 0 0 / 40%);
}

.notification.error {
background-image: url('../img/error.svg') ;
background-color: #fffafa;
Expand All @@ -37,4 +40,4 @@
font-size: 1.5rem;
opacity: 0.4;
cursor: pointer;
}
}
1 change: 0 additions & 1 deletion src/css/select.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ button.expand::after {
border: 0 solid var(--light);
box-shadow: var(--shadaw);
border-radius: var(--radius);
transition: all 300ms;
max-height: 0;
overflow: hidden;
z-index: 10;
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useChildRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export default function useChildRequest(): string {
const store = useStore() as Store;

useEffect(() => {
if (!childRequest) {
store.api.getChildRequest(store.ca as string).then((text: string) => {
if (!childRequest && store.ca ) {
store.api.getChildRequest(store.ca).then((text: string) => {
setChildRequest(text);
cache = text;
});
Expand Down
54 changes: 15 additions & 39 deletions src/hooks/useParentActions.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { ChangeEvent, FormEvent, useEffect, useState } from 'react';
import { FormEvent, useState } from 'react';
import { useRoute, useRouter } from 'react-router5';
import { NotificationType, Notification } from '../core/types';
import useChildRequest from '../hooks/useChildRequest';
import useNavigation from '../hooks/useNavigation';
import useTranslations from './useTranslations';
import useRequestActions from './useRequestActions';

export default function useParantActions() {
const childRequest = useChildRequest();
const router = useRouter();
const t = useTranslations();
const { route: { params } } = useRoute();
const navigate = useNavigation();
const childRequest = useChildRequest();
const [notification, setNotification] = useState<Notification | null>(null);
const [name, setName] = useState('');
const [request, setRequest] = useState('');
const [response, setResponse] = useState('');

useEffect(() => {
setRequest(childRequest);
}, [childRequest]);

const {
notification,
setNotification,
request,
dataUrl,
setRequest,
response,
setResponse,
onCopy,
handleUpload,
} = useRequestActions(childRequest);

const onSubmit = async (e: FormEvent) => {
e.preventDefault();
Expand All @@ -29,33 +32,6 @@ export default function useParantActions() {
router.navigate('cas.parents', { ca: params.ca });
};

const onCopy = () => {
const copyText = document.getElementById('request') as HTMLTextAreaElement | null;

if (copyText !== null) {
copyText.select();
copyText.setSelectionRange(0, 99999);
navigator.clipboard.writeText(copyText.value);

setNotification({
type: NotificationType.success,
message: t.common.copySuccess
});
}
};

const dataUrl = `data:application/xml;base64,${btoa(request)}`;

const handleUpload = (e: ChangeEvent<HTMLInputElement>) => {
if (e.currentTarget.files) {
const fileReader = new FileReader();
fileReader.onload = () => {
setResponse(fileReader.result as string);
};
fileReader.readAsText(e.currentTarget.files[0]);
}
};

return {
notification,
setNotification,
Expand Down
22 changes: 22 additions & 0 deletions src/hooks/usePublisherRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useEffect, useState } from 'react';
import Store from '../core/store';
import useStore from './useStore';

let cache = '';

export default function usePublisherRequest(): string {
const [publisherRequest, setPublisherRequest] = useState<string>(cache);
const store = useStore() as Store;

useEffect(() => {
if (!publisherRequest && store.ca ) {
store.api.getPublisherRequest(store.ca).then((text: string) => {
setPublisherRequest(text);
cache = text;
});
}
}, []);

return publisherRequest;
}

Loading

0 comments on commit 9244add

Please sign in to comment.