Skip to content

Commit

Permalink
Add RecordRequests React project and initial files
Browse files Browse the repository at this point in the history
  • Loading branch information
corovcam committed Jan 30, 2024
1 parent 98ad579 commit 4a15575
Show file tree
Hide file tree
Showing 26 changed files with 1,826 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{#def record #}

{% set record = record | to_dict %}

<div id="record-requests" data-record='{{ record | tojson }}'></div>

{%- block javascript %}
{{ super() }}
{{ webpack['oarepo_requests_ui_record_requests.js']}}
{%- endblock %}
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
This is the registration file for custom components. The components should not be included here,
but only referenced. The sample component below can be used to start up working on your own custom
component.
*/
/*
In the sample-component.js you can define your own javascript functions etc for your custom component.
Then import the file here
import "./sample-component.js"
*/

// This file will import the css templates for your custom components

import "../../less/oarepo_requests_ui/custom-components.less";
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { useState } from "react";
import PropTypes from "prop-types";

import { i18next } from "@translations/oarepo_requests_ui/i18next";

import Overridable from "react-overridable";
import { List, Segment, Header, Button } from "semantic-ui-react";

import { RequestModal } from "./RequestModal";

/**
* @typedef {import("../types").Request} Request
* @typedef {import("../types").RequestType} RequestType
*/

/**
* @param {{ requestTypes: RequestType[] }} props
*/
export const CreateRequestButtonGroup = ({ requestTypes }) => {
const createRequests = requestTypes.filter(requestType => requestType.links.actions?.create);

return (
<Segment>
<Header size="medium">{i18next.t("Create Request")}</Header>
<Button.Group vertical compact fluid basic>
{createRequests.map((requestType) => (
<RequestModal
key={requestType.id}
request={requestType}
requestModalType="create"
triggerButton={<Button icon="plus" title={i18next.t(requestType.name)} basic compact content={requestType.name} />}
/>
))}
</Button.Group>
</Segment>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useState, useRef, useEffect } from "react";
import PropTypes from "prop-types";

import { i18next } from "@translations/oarepo_requests_ui/i18next";
import { Dimmer, Loader, Segment, Modal, Button, Header, Icon, Grid, Input, List, Container, Message, Form, Divider } from "semantic-ui-react";
import { useFormikContext } from "formik";

import _isFunction from "lodash/isFunction";

import { CustomFields } from "react-invenio-forms";

/**
* @typedef {import("../types").RequestType} RequestType
* @typedef {import("formik").FormikConfig} FormikConfig
*/

/** @param {{ requestType: RequestType }} props */
export const CreateRequestModalContent = ({ requestType, extraPreSubmitEvent }) => {
const payloadUI = requestType?.payload_ui;

const { isSubmitting, isValid, handleSubmit } = useFormikContext();

const customSubmitHandler = (event) => {
_isFunction(extraPreSubmitEvent) && extraPreSubmitEvent(event);
handleSubmit(event);
}

return (
<>
{requestType?.description &&
<p id="request-modal-desc">
{requestType.description}
</p>
}
{payloadUI &&
<Segment>
<Form onSubmit={customSubmitHandler} id="request-form">
<CustomFields
// ref={cfRef}
config={payloadUI}
templateLoaders={[
(widget) => import(`@templates/custom_fields/${widget}.js`),
(widget) => import(`react-invenio-forms`)
]}
fieldPathPrefix="payload"
/>
<Divider hidden />
</Form>
</Segment>
}
</>
);
}

CreateRequestModalContent.propTypes = {
requestType: PropTypes.object.isRequired,
extraPreSubmitEvent: PropTypes.func
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { useState } from "react";
import PropTypes from "prop-types";

import Overridable, {
OverridableContext,
overrideStore,
} from "react-overridable";

import { i18next } from "@translations/oarepo_requests_ui/i18next";
import _sortBy from "lodash/sortBy";

import { List, Segment, Header, Button } from "semantic-ui-react";

import { CreateRequestButtonGroup, RequestListContainer } from ".";
import { RequestContextProvider } from "../contexts";

/**
* @typedef {import("../types").Request} Request
* @typedef {import("../types").RequestType} RequestType
*/

export const RecordRequests = ({ record }) => {
/** @type {RequestType[]} */
const requestTypes = record.request_types ?? [];

const requestsState = useState(_sortBy(record.requests, ["status_code"]) ?? []);

return (
<>
{/* Context for app - to not reload page after RequestModal submit */}
{/* <OverridableContext.Provider value={overrideStore.getAll()}> */}
<RequestContextProvider requests={requestsState}>
{requestTypes && (
<CreateRequestButtonGroup requestTypes={requestTypes} />
)}
{record?.requests && (
<RequestListContainer requestTypes={requestTypes} />
)}
</RequestContextProvider>
{/* </OverridableContext.Provider> */}
</>
);
}

RecordRequests.propTypes = {
record: PropTypes.object.isRequired,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from "react";
import PropTypes from "prop-types";

import { i18next } from "@translations/oarepo_requests_ui/i18next";
import { List, Segment, SegmentGroup, Header, Button } from "semantic-ui-react";
import _isEmpty from "lodash/isEmpty";

import { RequestModal } from ".";

/**
* @typedef {import("../types").Request} Request
* @typedef {import("../types").RequestTypeEnum} RequestTypeEnum
*/

/**
* @param {{ requests: Request[], requestModalType: RequestTypeEnum }} props
*/
export const RequestList = ({ requests, requestTypes, requestModalType }) => {
return (
<List link divided relaxed>
{requests.map((request) => {
let modalType = requestModalType;
if (_isEmpty(requestModalType)) {
if ("submit" in request.links?.actions) {
modalType = "submit";
} else if ("cancel" in request.links?.actions) {
modalType = "cancel";
}
}
return (
<RequestModal key={request.uuid} request={request} requestTypes={requestTypes} requestModalType={modalType}
triggerButton={
<List.Item as="a" key={request.id}>
{/* {request?.created && <List.Content floated="right" verticalAlign="bottom">{new Date(request.created)?.toLocaleString("cs-CZ")}</List.Content>} */}
<List.Content floated="right" verticalAlign="middle">
<div style={{ textAlign: "right" }}>{request?.status ?? i18next.t("No status")}</div>
<div>{new Date(request.created)?.toLocaleString("cs-CZ")}</div>
</List.Content>
<List.Content>
<List.Header>{request.name}</List.Header>
<List.Description>{request.description}</List.Description>
</List.Content>
</List.Item>
}
/>
)
})}
</List>
)
};

RequestList.propTypes = {
requests: PropTypes.array.isRequired,
requestTypes: PropTypes.array.isRequired,
requestModalType: PropTypes.oneOf(["create", "accept", "submit", "cancel"])
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useContext } from "react";
import PropTypes from "prop-types";

import { i18next } from "@translations/oarepo_requests_ui/i18next";
import { List, Segment, SegmentGroup, Header, Button } from "semantic-ui-react";

import { RequestList } from ".";
import { RequestContext } from "../contexts";

/**
* @typedef {import("../types").Request} Request
* @typedef {import("../types").RequestType} RequestType
*/
/**
* @param {{ requests: Request[], requestTypes: RequestType[] }} props
*/
export const RequestListContainer = ({ requestTypes }) => {
const TriggerButtonForRequest = ({ onClick, request }) => (
<List.Item as={Button} onClick={onClick} inverted basic fluid style={{ textAlign: "left" }}>
{/* <List.Content floated="right" verticalAlign="middle">
<RequestModal request={request} triggerButton={<Button compact size="medium" title={`${i18next.t("Open dialog for request")} ${request.id}`} content="Open Dialog" />} />
</List.Content> */}
<List.Content>
<List.Header>
{request.name}
</List.Header>
<List.Description>
{request.description}
</List.Description>
</List.Content>
</List.Item>
);

const [requests, setRequests] = useContext(RequestContext);

let requestsToApprove = [];
let otherRequests = [];
for (const request of requests) {
if ("accept" in request.links?.actions) {
requestsToApprove.push(request);
} else {
otherRequests.push(request);
}
}

const SegmentGroupOrEmpty = requestsToApprove.length > 0 && otherRequests.length > 0 ? SegmentGroup : <></>;

return (
<SegmentGroupOrEmpty>
<Segment className="requests-my-requests">
<Header size="medium">{i18next.t("My Requests")}</Header>
<RequestList requests={otherRequests} requestTypes={requestTypes} />
</Segment>
{requestsToApprove.length > 0 && (
<Segment className="requests-requests-to-approve">
<Header size="medium">{i18next.t("Requests to Approve")}</Header>
<RequestList requests={requestsToApprove} requestTypes={requestTypes} requestModalType="accept" />
</Segment>
)}
</SegmentGroupOrEmpty>
);
};
Loading

0 comments on commit 4a15575

Please sign in to comment.