Merge branch 'main' of github.com:kjannette/ax3Client into jdurante-1-stripe-paywall

This commit is contained in:
Jacob
2024-01-23 14:43:58 -05:00
14 changed files with 168 additions and 86 deletions

View File

@@ -6,6 +6,7 @@ import Button from "../../pageElements/Button";
import { AppContext } from "../../Hooks/useContext/appContext"; import { AppContext } from "../../Hooks/useContext/appContext";
import { AuthContext } from "../../Context/AuthProvider"; import { AuthContext } from "../../Context/AuthProvider";
import ProgressBar from "../../pageElements/ProgressBar"; import ProgressBar from "../../pageElements/ProgressBar";
import ConfirmModal from "../Modals/ConfirmModal";
const AccountPage = () => { const AccountPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -15,7 +16,7 @@ const AccountPage = () => {
const [docsGenerated, setDocsGenerated] = useState(null); const [docsGenerated, setDocsGenerated] = useState(null);
const [docsAllowed, setDocsAllowed] = useState(10); const [docsAllowed, setDocsAllowed] = useState(10);
const appUserId = group ? group.appUserId : null; const appUserId = group ? group.appUserId : null;
const [showModal, setShowModal] = useState(false);
function getDocumentsCount() { function getDocumentsCount() {
if (!appUserId) { if (!appUserId) {
return; return;
@@ -36,6 +37,15 @@ const AccountPage = () => {
return null; return null;
} }
const handleCancelSub = () => {
setShowModal(true);
try {
//API call(s)
} catch (err) {
console.log(err);
}
setShowModal(false);
};
const handleSupport = () => { const handleSupport = () => {
const supReq = "5ac45d12"; const supReq = "5ac45d12";
navigate(`/requestpage/${supReq}`); navigate(`/requestpage/${supReq}`);
@@ -178,7 +188,11 @@ const AccountPage = () => {
</div> </div>
) : null} ) : null}
<div className="account-subscriptions-actions"> <div className="account-subscriptions-actions">
<Button className="primary-button" labelText="Cancel Account" /> <Button
className="primary-button cancel-sub-button"
labelText="Cancel Subscription"
onClick={() => setShowModal(true)}
/>
</div> </div>
</div> </div>
</div> </div>
@@ -194,6 +208,14 @@ const AccountPage = () => {
<div>{telNumberFormat(group.telephone)}</div> <div>{telNumberFormat(group.telephone)}</div>
</div> </div>
</div> </div>
{showModal ? (
<ConfirmModal
onCancel={() => setShowModal(false)}
onConfirm={handleCancelSub}
buttonLabelText="Confirm Cancel"
modalText="Are you sure? Your subscription will end at the conclusion of the current billing period."
/>
) : null}
</div> </div>
); );
}; };

View File

@@ -584,7 +584,7 @@ const DocEditPage = () => {
const editingContent = () => { const editingContent = () => {
return ( return (
<> <>
{headerPicker()} <div> {headerPicker()}</div>
<div className="doc-editing-wrapper"> <div className="doc-editing-wrapper">
<div className="pleading-header">{headerString}</div> <div className="pleading-header">{headerString}</div>
<div> <div>

View File

@@ -171,7 +171,7 @@ const DocumentListPage = ({ perPage }) => {
if (!group) { if (!group) {
return null; return null;
} }
console.log("allDocs", allDocs);
const DesktopContent = () => { const DesktopContent = () => {
return ( return (
<> <>
@@ -204,6 +204,7 @@ const DocumentListPage = ({ perPage }) => {
parentCaseNumber={doc.parentCaseNumber} parentCaseNumber={doc.parentCaseNumber}
parentCaseName={doc.parentCaseName} parentCaseName={doc.parentCaseName}
parentCaseId={doc.parentCaseId} parentCaseId={doc.parentCaseId}
createdAt={doc.createdAt}
docType={doc.docType} docType={doc.docType}
documentId={doc.documentId} documentId={doc.documentId}
dateServed={doc.dateServed} dateServed={doc.dateServed}

View File

@@ -12,7 +12,11 @@ const NewYorkCaption = (props) => {
<div className="docedit-uppercol2"> <div className="docedit-uppercol2">
<span>{fetchedCase?.jurisdiction}</span> <span>{fetchedCase?.jurisdiction}</span>
<span>{fetchedCase?.venue}</span> <span>{fetchedCase?.venue}</span>
<div className="docedit-uppercol-divider"></div> <div className="pleading-divider-box">
<div>
-----------------------------------------------------------------------X
</div>
</div>
</div> </div>
<div className="docedit-uppercol3"></div> <div className="docedit-uppercol3"></div>
</div> </div>
@@ -44,7 +48,11 @@ const NewYorkCaption = (props) => {
<div className="content-spacer2"></div> <div className="content-spacer2"></div>
<div>Defendant(s)</div> <div>Defendant(s)</div>
</div> </div>
<div className="docedit-uppercol-divider"></div> <div className="pleading-divider-box">
<div>
----------------------------------------------------------------------------X
</div>
</div>
</div> </div>
<div className="docedit-header-col3"> <div className="docedit-header-col3">
<div className="scroll-button-box"> <div className="scroll-button-box">

View File

@@ -11,6 +11,7 @@ import { collection, setDoc, updateDoc, doc } from "firebase/firestore";
import { AppContext } from "../../Hooks/useContext/appContext.js"; import { AppContext } from "../../Hooks/useContext/appContext.js";
import { db } from "../../firebase"; import { db } from "../../firebase";
import { objectMap } from "../../Utils/Object"; import { objectMap } from "../../Utils/Object";
import { createCaseFields as fields } from "../../Constants/Fields/CreateCaseFields.js";
import "../../styles/modals.scss"; import "../../styles/modals.scss";
import { import {
getFormDataDefaults, getFormDataDefaults,
@@ -28,29 +29,10 @@ const CreateModal = ({ setShowModal, caseData }) => {
const appUserId = group.appUserId; const appUserId = group.appUserId;
const editCaseId = caseData?.id; const editCaseId = caseData?.id;
const isEditing = typeof editCaseId !== "undefined"; const isEditing = typeof editCaseId !== "undefined";
const fields = {
caption: { required: true, maxLength: 45 },
captionTwo: { required: true, maxLength: 45 },
caseNumber: { required: true, maxLength: 45 },
judge: { required: true, maxLength: 45 },
plaintiffParties: { required: true, maxLength: 45 },
defendantParties: { required: true, maxLength: 45 },
jurisdiction: { required: true, maxLength: 45 },
billingCode: { required: true, maxLength: 45 },
venue: { required: true, maxLength: 45 },
caseType: { required: true, maxLength: 45 },
trialDate: { required: true, maxLength: 45, type: "date" },
filedDate: { required: true, maxLength: 45, type: "date" },
contactFirstName: { required: true, maxLength: 45 },
contactLastName: { required: true, maxLength: 45 },
contactEmail: { required: true, maxLength: 45, type: "email" },
leadAttorneys: { required: true, maxLength: 45 },
lawFirm: { required: false, maxLength: 45 },
clientPosition: { required: true, default: "Defendant" },
};
const [isBusy, setIsBusy] = useState(false); const [isBusy, setIsBusy] = useState(false);
const [newCaseId, setNewCaseId] = useState();
const caseNumCopy =
group.state == "New York" ? "Index Number" : "Case Number";
const [data, setData] = useState( const [data, setData] = useState(
getFormDataDefaults(fields, isEditing ? caseData : undefined) getFormDataDefaults(fields, isEditing ? caseData : undefined)
); );
@@ -69,10 +51,8 @@ const CreateModal = ({ setShowModal, caseData }) => {
return hasErrors ? null : objectMap(({ value }) => value, newData); return hasErrors ? null : objectMap(({ value }) => value, newData);
}; };
async function saveCaseData() { async function saveCaseData(dataValues) {
const dataValues = validateData(); if (isBusy) {
if (dataValues === null) {
return; return;
} }
setIsBusy(true); setIsBusy(true);
@@ -95,6 +75,7 @@ const CreateModal = ({ setShowModal, caseData }) => {
const casesRef = collection(db, "cases"); const casesRef = collection(db, "cases");
await setDoc(doc(casesRef, `${caseId}`), caseData); await setDoc(doc(casesRef, `${caseId}`), caseData);
setShowModal(false); setShowModal(false);
return caseId;
} catch (err) { } catch (err) {
console.log("Error creating case:", err); console.log("Error creating case:", err);
} }
@@ -110,11 +91,26 @@ const CreateModal = ({ setShowModal, caseData }) => {
} }
}; };
const handleCreate = () => { const handleCreateCaseNavigation = (caseId) => {
if (!isBusy) { // console.log("caseId in handleCreateCaseNavigation", caseId);
void saveCaseData(); };
const handleCreateCase = (e) => {
e.preventDefault();
const dataValues = validateData();
let caseId;
if (dataValues === null) {
return;
} else {
try {
saveCaseData(dataValues).then((res) => {
const caseId = res;
navigate(`/casedetails/${caseId}`);
});
} catch (err) {
console.log(err);
}
} }
navigate("/dashboard");
}; };
const radioOptions = [ const radioOptions = [
@@ -180,7 +176,7 @@ const CreateModal = ({ setShowModal, caseData }) => {
<Row> <Row>
<Col className="mb-3"> <Col className="mb-3">
{isEditing ? ( {isEditing ? (
<div className="edit-modal-label">Index number</div> <div className="edit-modal-label">{caseNumCopy}</div>
) : ( ) : (
<></> <></>
)} )}
@@ -548,7 +544,7 @@ const CreateModal = ({ setShowModal, caseData }) => {
<Button <Button
className="primary-button" className="primary-button"
disabled={isBusy} disabled={isBusy}
onClick={handleCreate} onClick={handleCreateCase}
labelText="Save Case" labelText="Save Case"
/> />
</Modal.Footer> </Modal.Footer>

View File

@@ -42,8 +42,10 @@ const UploadModal = (props) => {
: process.env.REACT_APP_API_PROD; : process.env.REACT_APP_API_PROD;
async function saveToDb(uuidName) { async function saveToDb(uuidName) {
const createdAt = new Date();
const data = { const data = {
ownerId: appUserId, ownerId: appUserId,
createdAt: createdAt,
documentName: `${uuidName}.pdf`, documentName: `${uuidName}.pdf`,
docDescription: docDescription, docDescription: docDescription,
dateServed: dateServed, dateServed: dateServed,
@@ -124,6 +126,7 @@ const UploadModal = (props) => {
try { try {
const response = await processFile(); const response = await processFile();
console.log("response in upload modal", response);
if (response.res?.status !== 200) { if (response.res?.status !== 200) {
saveToDb(response.uuidName); saveToDb(response.uuidName);
return; return;

View File

@@ -262,7 +262,6 @@ const SignupPage = () => {
<Row key={`row${j}`}> <Row key={`row${j}`}>
{names.map((name, i) => ( {names.map((name, i) => (
<Col key={`${name}${i + fieldsChunkSize * j}`} className="mb-3"> <Col key={`${name}${i + fieldsChunkSize * j}`} className="mb-3">
{console.log("data, name", data, name)}
<TextInput <TextInput
name={name} name={name}
value={data[name].value} value={data[name].value}

View File

@@ -5,45 +5,41 @@ export const Steps = () => (
<h2 className="doc-header-text">Generate Discovery Responses</h2> <h2 className="doc-header-text">Generate Discovery Responses</h2>
<ol className="steps-list"> <ol className="steps-list">
<li> <li>
<strong>CREATE THE CASE: </strong>First, input the case to which the <strong>CREATE THE CASE: </strong> Click Cases in the top navigation
discovery request pertains. Click Cases in the top navigation bar to bar to go to the <strong>Case View</strong>. Click the Create New Case
go to the <strong>Case View</strong>. Click the Create New Case button button (lower right) to surface the Create Case Modal. Enter all
(lower right) to surface the Create Case Modal. Enter all relevant relevant information -- case name, case number, parties, etc. -- and
information -- case name, case number, parties, etc. -- and click Save click Save Case to open the new case's
Case. The modal will close, returning you to the{" "} <strong> Details View.</strong> Click “Upload Document”.
<strong>Case View.</strong>
</li> </li>
<li> <li>
On the newly-created case info card, click the View button (far right) <strong>UPLOAD YOUR DOCUMENT: </strong>You will now see the Document
to navigate to its <strong>Case Details View.</strong> Click Upload Upload Modal. Enter your discovery request's info: document title, i.e.
Document to surface the Document Upload Modal. Defendants Interrogatories, and (optionally) the date served. Drag
and drop the file, or click to select it from your computer's files,{" "}
{" "}
then click <strong> “Upload”.</strong>
<br /> The upload will begin. Parsing the document usually takes 1 to 2
minutes, but be patient - it may take up to 5.
</li> </li>
<li> <li>
<strong>UPLOAD A DOCUMENT: </strong>On the Document Upload Modal, enter <strong>GENERATE THE RESPONSE: </strong>From the Document List View,
the document title, i.e. Defendants Interrogatories, and (optionally) click the “Generate Responses'' button. Novdrafts Intellidraft AI will
the date served. Drag and drop or click to select the request document generate the response and automatically navigate to the interactive
from your files. <em>The document must be in .pdf format.</em> Document Editor. You can edit the responses and save progress any time
<br />{" "} by clicking the “Save Changes” button.
<p className="howto-special-para">
Click Upload, and will you automatically navigate to the Document
List View.
</p>{" "}
</li> </li>
<li> <li>
<strong>GENERATE THE DISCOVERY RESPONSE: </strong>Click the Generate <strong>DOWNLOAD YOUR RESPONSE DOCUMENT: </strong>Click the “Create
Responses'' button. Novdrafts Intellidraft AI will generate the .Docx' button to download your fully-formatted, ready-to-serve response.
response and automatically navigate to the interactive Document Editor.
You can edit the responses and save progress anytime by clicking the
Save Changes button. When you are satisfied, you can download a .docx
file by clicking the Create .docx File button.
</li> </li>
<ExclamationTriangle <ExclamationTriangle
style={{ color: "red", marginRight: "6px", marginBottom: "2px" }} style={{ color: "red", marginRight: "6px", marginBottom: "2px" }}
/> />
<strong>Pro tip:</strong> once you've created a case, you can skip most of <strong>Pro tip:</strong> Once you've created the case, you can skip step
these steps. Simply search for the case by entering the case number in the one. Simply search for the case by entering the case number in the search
search bar on your Dashboard, click "Express Document Upload" and follow bar on your Dashboard, click "Express Document Upload" and follow the
the prompts. prompts.
</ol> </ol>
<p className="steps-p"></p> <p className="steps-p"></p>
</div> </div>

View File

@@ -0,0 +1,20 @@
export const createCaseFields = {
caption: { required: true, maxLength: 45 },
captionTwo: { required: true, maxLength: 45 },
caseNumber: { required: true, maxLength: 45 },
judge: { required: true, maxLength: 45 },
plaintiffParties: { required: true, maxLength: 45 },
defendantParties: { required: true, maxLength: 45 },
jurisdiction: { required: true, maxLength: 45 },
billingCode: { required: true, maxLength: 45 },
venue: { required: true, maxLength: 45 },
caseType: { required: true, maxLength: 45 },
trialDate: { required: true, maxLength: 45, type: "date" },
filedDate: { required: true, maxLength: 45, type: "date" },
contactFirstName: { required: true, maxLength: 45 },
contactLastName: { required: true, maxLength: 45 },
contactEmail: { required: true, maxLength: 45, type: "email" },
leadAttorneys: { required: true, maxLength: 45 },
lawFirm: { required: false, maxLength: 45 },
clientPosition: { required: true, default: "Plaintiff" },
};

View File

@@ -96,8 +96,10 @@ export const handleFormDataChange = (e, name, data, fields) => {
return null; return null;
}; };
export const isFormDataHasErrors = (data) => export const isFormDataHasErrors = (data) => {
Object.values(data).reduce( console.log("data", data);
return Object.values(data).reduce(
(hasErrors, { error }) => hasErrors || error, (hasErrors, { error }) => hasErrors || error,
false false
); );
};

View File

@@ -4,7 +4,7 @@ import { FileEarmarkText } from "react-bootstrap-icons";
import TextInput from "../pageElements/TextInput"; import TextInput from "../pageElements/TextInput";
import Tooltip from "../pageElements/Tooltip"; import Tooltip from "../pageElements/Tooltip";
import { InfoCircle } from "react-bootstrap-icons"; import { InfoCircle } from "react-bootstrap-icons";
import { toDate } from "firebase/firestore";
export const InfoCard = (props) => { export const InfoCard = (props) => {
const { data, onEdit, onCancel, onSave, onChangeInput, isEditing, isBusy } = const { data, onEdit, onCancel, onSave, onChangeInput, isEditing, isBusy } =
props; props;
@@ -273,15 +273,20 @@ export const DocCard = (props) => {
handleNavigate, handleNavigate,
responseGenerations, responseGenerations,
confirmDelete, confirmDelete,
createdAt,
} = props; } = props;
const now = new Date().getTime();
const uploadedAt = createdAt.toDate();
const isReportable = Math.round((now - uploadedAt) / 60 / 1000) > 15;
const disabled = docType ? false : true;
const tooltipText =
"Parsing is taking a bit longer than usual. This usually resolves within 15 minutes. If it does not, click “report” and support will address the issue within 24 hours.";
const pendingText =
"Parsing in process, please be patient. This should resolve within 10 minutes.";
const report = (documentId, docType) => { const report = (documentId, docType) => {
console.log("report documentId, docType", documentId, docType); console.log("report documentId, docType", documentId, docType);
}; };
const tooltipText =
"Parsing is taking a bit longer than usual. This usually resolves within 10 - 15 minutes. If it does not, click “report” and support will address the issue within 24 hours.";
const disabled = docType ? false : true;
return ( return (
<div className="doc-card-container"> <div className="doc-card-container">
<div className="doc-card-input"> <div className="doc-card-input">
@@ -289,7 +294,13 @@ export const DocCard = (props) => {
<FileEarmarkText className="doc-file-icon" size="22px" /> <FileEarmarkText className="doc-file-icon" size="22px" />
</div> </div>
<div className="doc-col2"> <div className="doc-col2">
{title} {disabled ? <Tooltip text={tooltipText} /> : <></>} {title}{" "}
{disabled && isReportable ? <Tooltip text={tooltipText} /> : <></>}
{disabled && !isReportable ? (
<Tooltip text={pendingText} iconStyle="clock" />
) : (
<></>
)}
</div> </div>
{dateServed ? ( {dateServed ? (
<div className="doc-col3">Served: {dateServed}</div> <div className="doc-col3">Served: {dateServed}</div>
@@ -299,7 +310,7 @@ export const DocCard = (props) => {
<div className="doc-col4"> <div className="doc-col4">
<div className="jurisdiction-box">{parentCaseName}</div> <div className="jurisdiction-box">{parentCaseName}</div>
</div> </div>
{disabled ? ( {disabled && isReportable ? (
<div className="doc-col5"> <div className="doc-col5">
<Button <Button
labelText="Report" labelText="Report"
@@ -345,3 +356,17 @@ export const DocCard = (props) => {
</div> </div>
); );
}; };
/*
{disabled ? (
isReportable ? (
<Tooltip text={tooltipText} />
) : (
<Tooltip text={pendingText} iconStyle="clock" />
)
) : (
<></>
)}
*/

View File

@@ -1,18 +1,23 @@
import { useState } from "react"; import { useState } from "react";
import "../styles/tooltip.scss"; import "../styles/tooltip.scss";
import { InfoCircle } from "react-bootstrap-icons"; import { InfoCircle, Clock } from "react-bootstrap-icons";
function Tooltip(props) { function Tooltip(props) {
const [show, setShow] = useState(false); const [showMessage, setShowMessage] = useState(false);
const { text } = props; const { text, iconStyle } = props;
const showClock = iconStyle === "clock";
return ( return (
<div <div
className="tooltip-container" className="tooltip-container"
onMouseEnter={() => setShow(true)} onMouseEnter={() => setShowMessage(true)}
onMouseLeave={() => setShow(false)} onMouseLeave={() => setShowMessage(false)}
> >
<InfoCircle className="infocircle-icon" size="16px" /> {showClock ? (
{show ? <span className="tooltip-text">{text}</span> : <></>} <Clock className="infocircle-icon" size="16px" />
) : (
<InfoCircle className="infocircle-icon" size="16px" />
)}
{showMessage ? <span className="tooltip-text">{text}</span> : <></>}
</div> </div>
); );
} }

View File

@@ -87,6 +87,10 @@ $account-support-title-padding: 10px;
} }
} }
.cancel-sub-button {
width: 165px;
}
.account-support { .account-support {
display: grid; display: grid;
grid-template-columns: 2fr 1fr; grid-template-columns: 2fr 1fr;

View File

@@ -90,7 +90,8 @@
.docedit-header-row { .docedit-header-row {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
height: 210px; height: 230px;
margin-bottom: 12px;
color: #000000; color: #000000;
font-weight: 500; font-weight: 500;
overflow: hidden; overflow: hidden;
@@ -247,7 +248,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 60%; width: 60%;
margin: 22px auto; margin: 6px auto;
color: #4e4e4e; color: #4e4e4e;
} }