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 { AuthContext } from "../../Context/AuthProvider";
import ProgressBar from "../../pageElements/ProgressBar";
import ConfirmModal from "../Modals/ConfirmModal";
const AccountPage = () => {
const navigate = useNavigate();
@@ -15,7 +16,7 @@ const AccountPage = () => {
const [docsGenerated, setDocsGenerated] = useState(null);
const [docsAllowed, setDocsAllowed] = useState(10);
const appUserId = group ? group.appUserId : null;
const [showModal, setShowModal] = useState(false);
function getDocumentsCount() {
if (!appUserId) {
return;
@@ -36,6 +37,15 @@ const AccountPage = () => {
return null;
}
const handleCancelSub = () => {
setShowModal(true);
try {
//API call(s)
} catch (err) {
console.log(err);
}
setShowModal(false);
};
const handleSupport = () => {
const supReq = "5ac45d12";
navigate(`/requestpage/${supReq}`);
@@ -178,7 +188,11 @@ const AccountPage = () => {
</div>
) : null}
<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>
@@ -194,6 +208,14 @@ const AccountPage = () => {
<div>{telNumberFormat(group.telephone)}</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>
);
};

View File

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

View File

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

View File

@@ -12,7 +12,11 @@ const NewYorkCaption = (props) => {
<div className="docedit-uppercol2">
<span>{fetchedCase?.jurisdiction}</span>
<span>{fetchedCase?.venue}</span>
<div className="docedit-uppercol-divider"></div>
<div className="pleading-divider-box">
<div>
-----------------------------------------------------------------------X
</div>
</div>
</div>
<div className="docedit-uppercol3"></div>
</div>
@@ -44,7 +48,11 @@ const NewYorkCaption = (props) => {
<div className="content-spacer2"></div>
<div>Defendant(s)</div>
</div>
<div className="docedit-uppercol-divider"></div>
<div className="pleading-divider-box">
<div>
----------------------------------------------------------------------------X
</div>
</div>
</div>
<div className="docedit-header-col3">
<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 { db } from "../../firebase";
import { objectMap } from "../../Utils/Object";
import { createCaseFields as fields } from "../../Constants/Fields/CreateCaseFields.js";
import "../../styles/modals.scss";
import {
getFormDataDefaults,
@@ -28,29 +29,10 @@ const CreateModal = ({ setShowModal, caseData }) => {
const appUserId = group.appUserId;
const editCaseId = caseData?.id;
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 [newCaseId, setNewCaseId] = useState();
const caseNumCopy =
group.state == "New York" ? "Index Number" : "Case Number";
const [data, setData] = useState(
getFormDataDefaults(fields, isEditing ? caseData : undefined)
);
@@ -69,10 +51,8 @@ const CreateModal = ({ setShowModal, caseData }) => {
return hasErrors ? null : objectMap(({ value }) => value, newData);
};
async function saveCaseData() {
const dataValues = validateData();
if (dataValues === null) {
async function saveCaseData(dataValues) {
if (isBusy) {
return;
}
setIsBusy(true);
@@ -95,6 +75,7 @@ const CreateModal = ({ setShowModal, caseData }) => {
const casesRef = collection(db, "cases");
await setDoc(doc(casesRef, `${caseId}`), caseData);
setShowModal(false);
return caseId;
} catch (err) {
console.log("Error creating case:", err);
}
@@ -110,11 +91,26 @@ const CreateModal = ({ setShowModal, caseData }) => {
}
};
const handleCreate = () => {
if (!isBusy) {
void saveCaseData();
const handleCreateCaseNavigation = (caseId) => {
// console.log("caseId in handleCreateCaseNavigation", caseId);
};
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 = [
@@ -180,7 +176,7 @@ const CreateModal = ({ setShowModal, caseData }) => {
<Row>
<Col className="mb-3">
{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
className="primary-button"
disabled={isBusy}
onClick={handleCreate}
onClick={handleCreateCase}
labelText="Save Case"
/>
</Modal.Footer>

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ import { FileEarmarkText } from "react-bootstrap-icons";
import TextInput from "../pageElements/TextInput";
import Tooltip from "../pageElements/Tooltip";
import { InfoCircle } from "react-bootstrap-icons";
import { toDate } from "firebase/firestore";
export const InfoCard = (props) => {
const { data, onEdit, onCancel, onSave, onChangeInput, isEditing, isBusy } =
props;
@@ -273,15 +273,20 @@ export const DocCard = (props) => {
handleNavigate,
responseGenerations,
confirmDelete,
createdAt,
} = 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) => {
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 (
<div className="doc-card-container">
<div className="doc-card-input">
@@ -289,7 +294,13 @@ export const DocCard = (props) => {
<FileEarmarkText className="doc-file-icon" size="22px" />
</div>
<div className="doc-col2">
{title} {disabled ? <Tooltip text={tooltipText} /> : <></>}
{title}{" "}
{disabled && isReportable ? <Tooltip text={tooltipText} /> : <></>}
{disabled && !isReportable ? (
<Tooltip text={pendingText} iconStyle="clock" />
) : (
<></>
)}
</div>
{dateServed ? (
<div className="doc-col3">Served: {dateServed}</div>
@@ -299,7 +310,7 @@ export const DocCard = (props) => {
<div className="doc-col4">
<div className="jurisdiction-box">{parentCaseName}</div>
</div>
{disabled ? (
{disabled && isReportable ? (
<div className="doc-col5">
<Button
labelText="Report"
@@ -345,3 +356,17 @@ export const DocCard = (props) => {
</div>
);
};
/*
{disabled ? (
isReportable ? (
<Tooltip text={tooltipText} />
) : (
<Tooltip text={pendingText} iconStyle="clock" />
)
) : (
<></>
)}
*/

View File

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

View File

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

View File

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