Merge pull request #788 from OpenSignLabs/public_url

feat: create public profile URLs to add username and develop functionality to make public template from report
This commit is contained in:
prafull-opensignlabs
2024-05-30 17:53:07 +05:30
committed by GitHub
11 changed files with 592 additions and 52 deletions
+4 -1
View File
@@ -1578,7 +1578,10 @@ function PdfRequestFiles() {
handleClose={() => {
setIsCompleted((prev) => ({ ...prev, isModal: false }));
}}
reduceWidth={!isCompleted?.message}
reduceWidth={
!isCompleted?.message &&
"md:min-w-[440px] md:max-w-[400px]"
}
>
<div style={{ height: "100%", padding: 20 }}>
{isCompleted?.message ? (
+133 -43
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useRef } from "react";
import { Navigate, useNavigate } from "react-router-dom";
import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize";
@@ -27,6 +27,10 @@ function UserProfile() {
const [percentage, setpercentage] = useState(0);
const [isDisableDocId, setIsDisableDocId] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [publicUserName, setPublicUserName] = useState(
extendUser && extendUser?.[0]?.UserName
);
const previousPublicUserName = useRef(publicUserName);
const [company, setCompany] = useState(
extendUser && extendUser?.[0]?.Company
);
@@ -37,6 +41,7 @@ function UserProfile() {
const [otp, setOtp] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [isEmailVerified, setIsEmailVerified] = useState(false);
const [userNameError, setUserNameError] = useState("");
useEffect(() => {
getUserDetail();
}, []);
@@ -74,46 +79,72 @@ function UserProfile() {
}
}
};
//function to check public username already exist
const handleCheckPublicUserName = async () => {
try {
const res = await Parse.Cloud.run("getpublicusername", {
username: publicUserName
});
if (res) {
setIsLoader(false);
setUserNameError("user name already exist");
setTimeout(() => {
setUserNameError("");
}, 3000);
return res;
}
} catch (e) {
console.log("error in getpublicusername cloud function");
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoader(true);
let phn = Phone;
try {
const tour = Parse.Object.extend("_User");
const query = new Parse.Query(tour);
await query.get(UserProfile.objectId).then((object) => {
object.set("name", name);
object.set("ProfilePic", Image);
object.set("phone", phn || "");
object.save().then(
async (response) => {
if (response) {
let res = response.toJSON();
let rr = JSON.stringify(res);
localStorage.setItem("UserInformation", rr);
SetName(res.name);
SetPhone(res?.phone || "");
setImage(res.ProfilePic);
localStorage.setItem("username", res.name);
localStorage.setItem("profileImg", res.ProfilePic);
await updateExtUser({ Name: res.name, Phone: res?.phone || "" });
alert("Profile updated successfully.");
setEditMode(false);
navigate("/dashboard/35KBoSgoAK");
let phn = Phone,
res;
//condition to call cloud function when user change publicUserName
if (previousPublicUserName.current !== publicUserName) {
res = await handleCheckPublicUserName();
}
if (!res) {
setIsLoader(true);
try {
const userQuery = Parse.Object.extend("_User");
const query = new Parse.Query(userQuery);
await query.get(UserProfile.objectId).then((object) => {
object.set("name", name);
object.set("ProfilePic", Image);
object.set("phone", phn || "");
object.save().then(
async (response) => {
if (response) {
let res = response.toJSON();
let rr = JSON.stringify(res);
localStorage.setItem("UserInformation", rr);
SetName(res.name);
SetPhone(res?.phone || "");
setImage(res.ProfilePic);
localStorage.setItem("username", res.name);
localStorage.setItem("profileImg", res.ProfilePic);
await updateExtUser({
Name: res.name,
Phone: res?.phone || ""
});
alert("Profile updated successfully.");
setEditMode(false);
setIsLoader(false);
//navigate("/dashboard/35KBoSgoAK");
}
},
(error) => {
alert("Something went wrong.");
console.error("Error while updating tour", error);
setIsLoader(false);
}
},
(error) => {
alert("Something went wrong.");
console.error("Error while updating tour", error);
setIsLoader(false);
}
);
});
} catch (error) {
console.log("err", error);
);
});
} catch (error) {
console.log("err", error);
}
}
};
@@ -122,14 +153,15 @@ function UserProfile() {
const extClass = localStorage.getItem("extended_class");
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
const ExtUserId = extData[0].objectId;
const body = {
Phone: obj?.Phone || "",
Name: obj.Name,
HeaderDocId: isDisableDocId,
JobTitle: jobTitle,
Company: company
Company: company,
UserName: publicUserName || ""
};
await axios.put(
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId,
body,
@@ -147,7 +179,6 @@ function UserProfile() {
const json = JSON.parse(JSON.stringify([res]));
const extRes = JSON.stringify(json);
localStorage.setItem("Extand_Class", extRes);
// console.log("updateRes ", updateRes);
};
@@ -241,6 +272,27 @@ function UserProfile() {
setOtpLoader(false);
alert("OTP sent on you email");
};
//function to handle onchange username and restrict 6-characters username for free users
const handleOnchangeUserName = (e) => {
const value = e.target.value;
if (value.length > 6 && !isSubscribe) {
setUserNameError("Please upgrade to allow more than 6 characters.");
setTimeout(() => {
setUserNameError("");
}, 2000);
} else {
setPublicUserName(e.target.value);
}
};
const handleCancel = () => {
setEditMode(false);
SetName(localStorage.getItem("username"));
SetPhone(UserProfile && UserProfile.phone);
setImage(localStorage.getItem("profileImg"));
setPublicUserName(extendUser && extendUser?.[0]?.UserName);
setCompany(extendUser && extendUser?.[0]?.Company);
setJobTitle(extendUser?.[0]?.JobTitle);
};
return (
<React.Fragment>
<Title title={"Profile"} />
@@ -262,7 +314,14 @@ function UserProfile() {
></div>
</div>
) : (
<div className="flex justify-center items-center w-full">
<div className="flex justify-center items-center w-full relative">
{userNameError && (
<div
className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`}
>
{userNameError}
</div>
)}
<div className="bg-white flex flex-col justify-center shadow rounded w-[450px]">
<div className="flex flex-col justify-center items-center my-4">
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
@@ -390,6 +449,37 @@ function UserProfile() {
)}
</span>
</li>
{!isEnableSubscription && (
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">
Public profile :{" "}
<Tooltip
message={`this is your public URL. Copy or share it
with the signer, and you will be able to see
all your publicly set templates.`}
/>
</span>
<div className="flex items-center">
<span>opensign-me.vercel.app/</span>
{editmode ? (
<input
onChange={handleOnchangeUserName}
value={publicUserName}
disabled={!editmode}
placeholder="enter user name"
className="border-[1px] border-gray-200 rounded-[3px]"
/>
) : (
<input
value={extendUser?.[0]?.UserName}
disabled
placeholder="enter user name"
className="border-[1px] border-gray-200 rounded-[3px]"
/>
)}
</div>
</li>
)}
<li className="border-y-[1px] border-gray-300 break-all">
<div className="flex justify-between items-center py-2">
<span
@@ -460,7 +550,7 @@ function UserProfile() {
type="button"
onClick={() => {
if (editmode) {
setEditMode(false);
handleCancel();
} else {
navigate("/changepassword");
}
@@ -4,14 +4,19 @@ import { useNavigate } from "react-router-dom";
import axios from "axios";
import ModalUi from "./ModalUi";
import AddSigner from "../components/AddSigner";
import { modalSubmitBtnColor, modalCancelBtnColor } from "../constant/const";
import {
modalSubmitBtnColor,
modalCancelBtnColor,
isEnableSubscription
} from "../constant/const";
import Alert from "./Alert";
import Tooltip from "./Tooltip";
import { RWebShare } from "react-web-share";
import Tour from "reactour";
import Parse from "parse";
import { saveAs } from "file-saver";
import { replaceMailVaribles } from "../constant/Utils";
import { copytoData, replaceMailVaribles } from "../constant/Utils";
import Confetti from "react-confetti";
import EditorToolbar, {
module1,
formats
@@ -36,6 +41,7 @@ const ReportTable = (props) => {
const [isTour, setIsTour] = useState(false);
const [tourStatusArr, setTourStatusArr] = useState([]);
const [isResendMail, setIsResendMail] = useState({});
const [isMakePublic, setIsMakePublic] = useState({});
const [mail, setMail] = useState({ subject: "", body: "" });
const [userDetails, setUserDetails] = useState({});
const [isNextStep, setIsNextStep] = useState({});
@@ -43,6 +49,12 @@ const ReportTable = (props) => {
const [templateDeatils, setTemplateDetails] = useState({});
const [placeholders, setPlaceholders] = useState([]);
const [isLoader, setIsLoader] = useState({});
const [selectedPublicRole, setSelectedPublicRole] = useState("");
const [isCelebration, setIsCelebration] = useState(false);
const [currentLists, setCurrentLists] = useState([]);
const [isPublic, setIsPublic] = useState({});
const [isPublicProfile, setIsPublicProfile] = useState({});
const [publicUserName, setIsPublicUserName] = useState("");
const [isViewShare, setIsViewShare] = useState({});
const startIndex = (currentPage - 1) * props.docPerPage;
const { isMoreDocs, setIsNextRecord } = props;
@@ -234,8 +246,19 @@ const ReportTable = (props) => {
// Get current list
const indexOfLastDoc = currentPage * props.docPerPage;
const indexOfFirstDoc = indexOfLastDoc - props.docPerPage;
// `currentLists` is total record render on current page
const currentLists = props.List?.slice(indexOfFirstDoc, indexOfLastDoc);
useEffect(() => {
// `currentLists` is total record render on current page
const currentList = props.List?.slice(indexOfFirstDoc, indexOfLastDoc);
//check public template and save in a object to show public and private template
setIsPublic(
currentList.reduce((acc, item) => {
acc[item.objectId] = item?.IsPublic || false;
return acc;
}, {})
);
setCurrentLists(currentList);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [indexOfLastDoc, indexOfFirstDoc]);
// Change page
const paginateFront = () => setCurrentPage(currentPage + 1);
@@ -297,9 +320,16 @@ const ReportTable = (props) => {
setActLoader({});
}
};
const handleClose = () => {
const handleClose = (item) => {
setIsRevoke({});
setIsDeleteModal({});
setIsMakePublic({});
setSelectedPublicRole("");
setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: !prevStates[item.objectId]
}));
setIsPublicProfile({});
};
const handleShare = (item) => {
@@ -687,9 +717,118 @@ const ReportTable = (props) => {
setTimeout(() => setIsAlert(false), 1500);
}
};
//function to make template public and set public role
const handlePublicTemplate = async (item) => {
if (selectedPublicRole || !isPublic[item.objectId]) {
setActLoader({ [item.objectId]: true });
setIsMakePublic(false);
try {
const res = await Parse.Cloud.run("createpublictemplate", {
templateid: item.objectId,
ispublic: isPublic[item.objectId],
publicrole: [selectedPublicRole]
});
if (res.status === "success") {
setIsAlert(true);
setTimeout(() => setIsAlert(false), 1500);
if (isPublic[item.objectId]) {
setAlertMsg({
type: "success",
message: "You have successfully made the template public."
});
setIsCelebration(true);
setTimeout(() => {
setIsCelebration(false);
}, 5000);
setIsPublicProfile({ [item.objectId]: isPublic[item.objectId] });
} else {
setAlertMsg({
type: "success",
message: "You have successfully made the template private."
});
setSelectedPublicRole("");
}
const updateList = props.List.map((x) =>
x.objectId === item.objectId
? { ...x, IsPublic: isPublic[item.objectId] }
: x
);
props.setList(updateList);
setActLoader({});
}
} catch (e) {
console.log("error in createpublictemplate", e);
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "Something went wrong, Please try again later!"
});
setTimeout(() => setIsAlert(false), 1500);
setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: !prevStates[item.objectId]
}));
}
} else {
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "You need to select a role for the public signers."
});
setTimeout(() => setIsAlert(false), 1500);
}
};
const handleViewSigners = (item) => {
setIsViewShare({ [item.objectId]: true });
};
//function to handle change template status is public or private
const handlePublicChange = async (e, item) => {
const getPlaceholder = item?.Placeholders;
//condiiton to check role is exist or not
if (getPlaceholder && getPlaceholder.length > 0) {
let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
const userName = extendUser[0]?.UserName;
setIsPublicUserName(extendUser[0]?.UserName);
//condition to check user have public url or not
if (userName) {
setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: e.target.checked
}));
const getPlaceholder = item?.Placeholders;
if (getPlaceholder.length === 1) {
setSelectedPublicRole(getPlaceholder[0]?.Role);
}
setIsMakePublic({ [item.objectId]: true });
} else {
setIsPublicProfile({ [item.objectId]: true });
}
} else {
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "Please assign at least one role to make this template public."
});
setTimeout(() => setIsAlert(false), 3000);
}
};
//function to copy public profile links
const copytoProfileLink = () => {
const url = `https://opensign-me.vercel.app/${publicUserName}`;
copytoData(url);
setIsAlert(true);
setAlertMsg({
type: "success",
message: "Copied."
});
setTimeout(() => setIsAlert(false), 1500);
};
return (
<div className="relative">
{Object.keys(actLoader)?.length > 0 && (
@@ -701,6 +840,11 @@ const ReportTable = (props) => {
</div>
)}
<div className="p-2 overflow-x-scroll w-full bg-white rounded-md">
{isCelebration && (
<div style={{ position: "relative", zIndex: "1000" }}>
<Confetti width={window.innerWidth} height={window.innerHeight} />
</div>
)}
{isAlert && <Alert type={alertMsg.type}>{alertMsg.message}</Alert>}
{props.tourData && props.ReportName === "Templates" && (
<Tour
@@ -747,6 +891,9 @@ const ReportTable = (props) => {
{props.actions?.length > 0 && (
<th className="px-4 py-2 font-thin">Action</th>
)}
{props.ReportName === "Templates" && !isEnableSubscription && (
<th className="px-4 py-2 font-thin">Public</th>
)}
</tr>
</thead>
<tbody className="text-[12px]">
@@ -1186,6 +1333,170 @@ const ReportTable = (props) => {
</ModalUi>
)}
</td>
{!isEnableSubscription && (
<td className=" pl-[20px] py-2 ">
{props.ReportName === "Templates" && (
<div className=" flex flex-row-">
<label
className={
"cursor-pointer relative inline-flex items-center mb-0"
}
>
<input
checked={isPublic?.[item.objectId]}
onChange={(e) => handlePublicChange(e, item)}
type="checkbox"
value=""
className="sr-only peer"
/>
<div className="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-black rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-black peer-checked:bg-blue-600"></div>
</label>
</div>
)}
{isMakePublic[item.objectId] && (
<ModalUi
isOpen
title={
isPublic[item.objectId]
? "Make template public"
: "Make template private"
}
handleClose={() => {
setIsMakePublic({});
setSelectedPublicRole("");
setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: !prevStates[item.objectId]
}));
}}
>
<div className="m-[20px]">
<div className="font-normal text-black">
<p className="text-lg">
{" "}
{isPublic[item.objectId]
? `Are you sure you want tof make this templat public ?`
: `Are you sure you want to make this template private? This will remove it from your public profile ?`}
</p>
{isPublic[item.objectId] && (
<div className="flex mt-2 gap-2 md:items-center">
<p className="text-[15px]">
Public role :{" "}
</p>
{item?.Placeholders?.length > 1 ? (
<select
className="w-[60%] md:w-[70%] border-[1px] border-gray-200 rounded-sm p-[2px]"
name="textvalidate"
value={selectedPublicRole}
onChange={(e) =>
setSelectedPublicRole(
e.target.value
)
}
>
<option
disabled
style={{ fontSize: "13px" }}
value=""
>
Select...
</option>
{item?.Placeholders.map(
(data, ind) => {
return (
<option
style={{ fontSize: "13px" }}
key={ind}
value={data?.Role}
>
{data?.Role}
</option>
);
}
)}
</select>
) : (
<div className="w-[60%] md:w-[70%] border-[1px] border-gray-200 rounded-sm p-[2px]">
{item?.Placeholders[0]?.Role}
</div>
)}
</div>
)}
</div>
<hr className="bg-[#ccc] mt-2 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handlePublicTemplate(item)}
className="px-4 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
style={{
backgroundColor: modalSubmitBtnColor
}}
>
Submit
</button>
<button
onClick={() => handleClose(item)}
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
style={{
backgroundColor: modalCancelBtnColor
}}
>
No
</button>
</div>
</div>
</ModalUi>
)}
{isPublicProfile[item.objectId] && (
<ModalUi
isOpen
title={"Public URL"}
handleClose={() => {
setIsPublicProfile({});
}}
reduceWidth={"md:min-w-[440px] md:max-w-[400px]"}
>
<div className="m-[20px]">
{publicUserName ? (
<div className="font-normal text-black">
<p>
Heres your public URL. Copy or share it
with the signer, and you will be able to
see all your publicly set templates.
</p>
<div className=" flex items-center gap-5 mt-2 p-[2px] w-[75%]">
<span className="font-bold">
Public URL :{" "}
</span>
<span
onClick={() => copytoProfileLink()}
className="underline underline-offset-2 cursor-pointer"
>{`https://opensign.me/${publicUserName}`}</span>
</div>
</div>
) : (
<div className="font-normal text-black">
<p>
Please add your public URL, and you will
be able to make a public template.
</p>
<button
className="px-4 py-1.5 mt-3 text-white rounded shadow-md text-center focus:outline-none "
style={{
backgroundColor: modalSubmitBtnColor
}}
onClick={() => navigate("/profile")}
>
Add
</button>
</div>
)}
</div>
</ModalUi>
)}
</td>
)}
</tr>
)
)}
+1 -3
View File
@@ -19,9 +19,7 @@ const ModalUi = ({
<div className="fixed z-[999] top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]">
<div
className={`${
reduceWidth
? "md:min-w-[440px] md:max-w-[400px]"
: "md:min-w-[500px]"
reduceWidth || "md:min-w-[500px]"
} fixed z-[1000] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-sm bg-white rounded shadow-md max-h-90 min-w-[90%] overflow-y-auto hide-scrollbar `}
>
{showHeader && (
+6
View File
@@ -39,6 +39,9 @@ import encryptedpdf from './parsefunction/encryptedPdf.js';
import { getSignedUrl } from './parsefunction/getSignedUrl.js';
import createBatchDocs from './parsefunction/createBatchDocs.js';
import linkContactToDoc from './parsefunction/linkContactToDoc.js';
import CreatePublicTemplate from './parsefunction/CreatePublicTemplate.js';
import GetPublicUserName from './parsefunction/GetPublicUserName.js';
import GetPublicTemplate from './parsefunction/GetPublicTemplate.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
@@ -88,3 +91,6 @@ Parse.Cloud.define('encryptedpdf', encryptedpdf);
Parse.Cloud.define('getsignedurl', getSignedUrl);
Parse.Cloud.define('batchdocuments', createBatchDocs);
Parse.Cloud.define('linkcontacttodoc', linkContactToDoc);
Parse.Cloud.define('createpublictemplate', CreatePublicTemplate);
Parse.Cloud.define('getpublicusername', GetPublicUserName);
Parse.Cloud.define('getpublictemplate', GetPublicTemplate);
@@ -0,0 +1,41 @@
export default async function CreatePublicTemplate(request) {
const templateid = request.params.templateid;
const ispublic = request.params.ispublic;
const publicrole = request.params.publicrole;
try {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
const userId = request?.user && request?.user?.id;
if (templateid) {
const updateTemplate = new Parse.Object('contracts_Template');
updateTemplate.id = templateid;
if (ispublic) {
updateTemplate.set('PublicRole', publicrole);
}
updateTemplate.set('IsPublic', ispublic);
const Acl = new Parse.ACL();
if (ispublic) {
Acl.setPublicReadAccess(true);
}
Acl.setReadAccess(userId, true);
Acl.setWriteAccess(userId, true);
updateTemplate.setACL(Acl);
const savedObject = await updateTemplate.save(null, { useMasterKey: true });
const res = savedObject.toJSON();
if (res) {
return {
status: 'success',
};
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
}
}
} catch (err) {
const code = err.code || 400;
const msg = err.message;
const error = new Parse.Error(code, msg);
throw error;
}
}
@@ -0,0 +1,32 @@
async function GetPublicTemplate(request) {
try {
const username = request.params.username;
if (username) {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('UserName', username);
const res = await extUserQuery.first({ useMasterKey: true });
const userId = res.get('UserId').id;
if (userId) {
const templatQuery = new Parse.Query('contracts_Template');
templatQuery.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
templatQuery.equalTo('IsPublic', true);
const getTemplate = await templatQuery.find({ useMasterKey: true });
return getTemplate;
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
}
} catch (err) {
const code = err.code || 400;
const msg = err.message;
const error = new Parse.Error(code, msg);
throw error;
}
}
export default GetPublicTemplate;
@@ -0,0 +1,23 @@
async function GetPublicUserName(request) {
try {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
const username = request.params.username;
if (username) {
const userQuery = new Parse.Query('contracts_Users');
userQuery.equalTo('UserName', username);
const res = await userQuery.first({ useMasterKey: true });
return res;
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
}
}
} catch (err) {
const code = err.code || 400;
const msg = err.message;
const error = new Parse.Error(code, msg);
throw error;
}
}
export default GetPublicUserName;
@@ -334,6 +334,7 @@ export default function reportJson(id, userId) {
'Signers.Email',
'Signers.Phone',
'Placeholders',
'IsPublic',
],
};
default:
@@ -0,0 +1,35 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
// TODO: set className here
const className = 'contracts_Template';
const schema = new Parse.Schema(className);
schema.addArray('PublicRole');
schema.addBoolean('IsPublic');
// TODO: Set the schema here
// Example:
// schema.addString('name').addNumber('cash');
return schema.update();
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
// TODO: set className here
const className = 'contracts_Template';
const schema = new Parse.Schema(className);
schema.deleteField('PublicRole');
schema.deleteField('IsPublic');
// TODO: Set the schema here
// Example:
// schema.deleteField('name').deleteField('cash');
return schema.update();
};
Binary file not shown.