This commit is contained in:
npalOmega 2026-01-21 16:25:57 -06:00
parent 933e93c845
commit 8417d708ac
2 changed files with 811 additions and 166 deletions

View file

@ -1,180 +1,152 @@
/* Wrapper & card */
.contactFormWrapper {
display: flex;
justify-content: center;
padding: 25px 20px;
background: #f5f6fa;
/* Contact Form Page */
.contactFormPage {
background: #f5f5f5;
min-height: 100vh;
font-family: "Roboto", "Open Sans", Arial, sans-serif;
padding: 0;
}
.formCard {
background: #fff;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
width: 100%;
max-width: 1100px;
padding: 20px 22px 26px;
border: 1px solid #e6e9ee;
.panel {
margin-bottom: 20px;
background-color: #fff;
border: 1px solid transparent;
border-radius: 4px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
}
/* Header */
.formHeader {
padding-bottom: 10px;
border-bottom: 1px solid #e9ecef;
margin-bottom: 16px;
}
.formHeader h2 {
margin: 0;
color: #1f3b4d;
font-size: 20px;
font-weight: 600;
.panel-default {
border-color: #ddd;
}
/* Section titles */
.sectionTitle {
margin: 0 0 12px;
color: #2b2f3b;
font-size: 13px;
.panel-connected {
border-radius: 0;
border: none;
box-shadow: none;
}
.panel-body {
padding: 15px;
}
/* Breadcrumb */
.breadcrumb {
padding: 8px 15px;
margin-bottom: 20px;
list-style: none;
background-color: #f5f5f5;
border-radius: 4px;
}
.breadcrumb-alt {
background-color: white;
border-bottom: 1px solid #e5e5e5;
padding: 10px 20px;
margin: 0 0 20px 0;
border-radius: 0;
}
.breadcrumb > li {
display: inline-block;
}
.breadcrumb > li + li:before {
content: ">\00a0";
padding: 0 5px;
color: #ccc;
}
.breadcrumb > li a {
color: #0088cc;
text-decoration: none;
}
.breadcrumb > li a:hover {
text-decoration: underline;
}
.breadcrumb > .active {
color: #00a0d1;
font-weight: bold;
cursor: default;
}
/* Container */
.container {
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
/* Form Header */
.header {
margin-bottom: 15px;
}
.heading {
margin: 0 0 10px 0;
font-weight: 300;
line-height: 1.1;
color: #333;
}
.heading.thin {
font-weight: 300;
font-size: 24px;
display: flex;
align-items: center;
gap: 10px;
}
.heading.text-uppercase {
text-transform: uppercase;
letter-spacing: 0.6px;
font-weight: 700;
}
/* Account widget (assignment) */
.account-widget {
display: flex;
gap: 14px;
align-items: center;
padding: 12px;
border-radius: 4px;
background: #fbfcfd;
border: 1px dashed #d6e6ef;
color: #0a4b6b;
}
.account-icon {
width: 48px;
height: 48px;
background: #e9f4fb;
color: #0077a8;
border-radius: 4px;
display:flex;
align-items:center;
justify-content:center;
font-size:20px;
}
.account-info .account-title {
font-weight:700;
}
.account-info .account-action {
color: #0b63a7;
font-weight:700;
margin-top:4px;
}
.account-sub {
color:#667085;
font-size:12px;
margin-top:4px;
}
/* Form layout */
.contactFormBody {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 12px 18px;
align-items: start;
margin-bottom: 6px;
}
/* Form groups */
.formGroup {
display: flex;
flex-direction: column;
margin-bottom: 8px;
}
.formGroup.fullWidth {
grid-column: 1 / -1;
}
/* Labels and controls */
.formLabel {
font-size: 13px;
font-weight: 600;
color: #253244;
margin-bottom: 6px;
}
.required { color: #d93025; margin-left: 4px; }
.formControl {
display: block;
width: 100%;
height: 36px;
padding: 6px 10px;
font-size: 13px;
color: #1f2937;
background: #fff;
border: 1px solid #d6dbe4;
border-radius: 4px;
box-sizing: border-box;
}
.formControl:focus {
outline: none;
border-color: #0b78b0;
box-shadow: 0 0 0 4px rgba(11,120,176,0.08);
}
/* Hints / muted */
.muted {
color: #6b7280;
font-size: 13px;
margin-bottom: 8px;
}
.formHint {
font-size: 12px;
color: #6b7280;
margin-top: 6px;
}
/* Form actions */
.formActions {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid #e9eef3;
padding-top: 14px;
margin-top: 14px;
}
.formNote {
color: #555;
font-size: 13px;
}
.btnPrimary {
background-color: #198754;
border: 1px solid #198754;
color: #fff;
padding: 10px 18px;
font-size: 14px;
border-radius: 4px;
cursor: pointer;
}
.btnPrimary:disabled {
background-color: #c9c9c9;
border-color: #c9c9c9;
cursor: not-allowed;
margin-bottom: 15px;
}
/* Responsive adjustments */
@media (max-width: 992px) {
.contactFormBody {
grid-template-columns: repeat(2, 1fr);
}
.text-primary {
color: #0088cc;
}
@media (max-width: 640px) {
.contactFormBody {
grid-template-columns: 1fr;
}
.formCard {
padding: 16px;
}
.fa-stack {
position: relative;
display: inline-block;
width: 2em;
height: 2em;
line-height: 2em;
vertical-align: middle;
}
.fa-stack-1x,
.fa-stack-2x {
position: absolute;
left: 0;
width: 100%;
text-align: center;
}
.fa-stack-2x {
font-size: 2em;
}
.fa-stack-1x {
font-size: 1em;
line-height: inherit;
}
.fa-inverse {
color: #fff;
}
/* Form Groups */
.form-group {
margin-bottom: 15px;
position: relative;
}
/* Row and Columns */
.row {
margin-right: -15px;
margin-left: -15px;
display: flex;
flex-wrap: wrap;
}

View file

@ -0,0 +1,673 @@
import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import "./ContactFormPage.css";
const ContactFormPage = () => {
const navigate = useNavigate();
const { id } = useParams();
const isEdit = Boolean(id);
const [formData, setFormData] = useState({
firstName: "",
middleName: "",
lastName: "",
title: "",
contactType: "",
gender: "",
dateOfBirth: "",
territory: "",
owner: "",
privacySetting: "PublicReadWrite",
about: "",
tags: "",
street: "",
streetLineTwo: "",
city: "",
zip: "",
country: "US",
state: "",
county: "",
timeZone: "Eastern Standard Time",
smsAddress: "",
website: "",
facebookUrl: "",
linkedInUrl: "",
twitterUrl: "",
});
const [loading, setLoading] = useState(false);
useEffect(() => {
if (isEdit) {
// TODO: Fetch contact data by ID
// For now, using mock data
setFormData({
firstName: "John",
middleName: "A",
lastName: "Doe",
title: "Manager",
contactType: "",
gender: "",
dateOfBirth: "",
territory: "",
owner: "",
privacySetting: "PublicReadWrite",
about: "",
tags: "",
street: "",
streetLineTwo: "",
city: "",
zip: "",
country: "US",
state: "",
county: "",
timeZone: "Eastern Standard Time",
smsAddress: "",
website: "",
facebookUrl: "",
linkedInUrl: "",
twitterUrl: "",
});
}
}, [isEdit, id]);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
const handleSave = async (e) => {
e.preventDefault();
setLoading(true);
try {
// TODO: Implement actual save logic
console.log("Saving contact:", formData);
setTimeout(() => {
setLoading(false);
navigate("/contacts");
}, 1000);
} catch (error) {
console.error("Error saving contact:", error);
setLoading(false);
}
};
// eslint-disable-next-line no-unused-vars
const handleCancel = () => {
navigate("/contacts");
};
return (
<div className="contactFormPage">
<div className="panel panel-default panel-connected">
<div className="panel-body">
<ol className="breadcrumb breadcrumb-alt">
<li>
<a
href="#"
onClick={(e) => {
e.preventDefault();
navigate("/contacts");
}}
>
Contacts
</a>
</li>
<li className="active">{isEdit ? "Edit" : "Create"}</li>
</ol>
<div className="container">
<form onSubmit={handleSave}>
{/* Page Header */}
<div className="form-group">
<div className="header">
<h3 className="heading thin">
<span className="fa-stack" style={{ fontSize: "0.9em" }}>
<i className="fa fa-square fa-stack-2x text-primary"></i>
<i
className={`fa fa-${
isEdit ? "edit" : "plus"
} fa-stack-1x fa-inverse`}
></i>
</span>
<span>
{isEdit ? "Edit a Contact" : "Create a Contact"}
</span>
</h3>
</div>
</div>
{/* General Details Section */}
<div className="form-group">
<div className="header">
<h5 className="heading text-uppercase">
<strong>General Details</strong>
</h5>
</div>
<div className="row">
<div className="col-sm-5">
<div className="form-group">
<span className="input-counter">50</span>
<label className="required-field" htmlFor="firstName">
First Name
</label>
<input
className="form-control"
type="text"
id="firstName"
name="firstName"
value={formData.firstName}
onChange={handleChange}
placeholder="Enter first name"
required
/>
</div>
</div>
<div className="col-sm-2">
<div className="form-group">
<span className="input-counter">50</span>
<label htmlFor="middleName">Middle Name</label>
<input
className="form-control"
type="text"
id="middleName"
name="middleName"
value={formData.middleName}
onChange={handleChange}
placeholder="Enter middle name"
/>
</div>
</div>
<div className="col-sm-5">
<div className="form-group">
<span className="input-counter">50</span>
<label className="required-field" htmlFor="lastName">
Last Name
</label>
<input
className="form-control"
type="text"
id="lastName"
name="lastName"
value={formData.lastName}
onChange={handleChange}
placeholder="Enter last name"
required
/>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-6">
<div className="form-group">
<span className="input-counter">50</span>
<label htmlFor="title">Title</label>
<input
className="form-control"
type="text"
id="title"
name="title"
value={formData.title}
onChange={handleChange}
placeholder="Enter a title"
/>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<label htmlFor="contactType">Contact Type</label>
<select
className="form-control"
id="contactType"
name="contactType"
value={formData.contactType}
onChange={handleChange}
>
<option value="">Select a Contact Type</option>
<option value="Corporate">Corporate</option>
<option value="Store Mgr">Store Mgr</option>
<option value="Facilities">Facilities</option>
<option value="Area Leader">Area Leader</option>
</select>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<label htmlFor="gender">Gender</label>
<select
className="form-control"
id="gender"
name="gender"
value={formData.gender}
onChange={handleChange}
>
<option value="">Select a gender</option>
<option value="Male">Male</option>
<option value="Female">Female</option>
<option value="Not specified">Not specified</option>
<option value="Other">Other</option>
</select>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<label htmlFor="dateOfBirth">Date of Birth</label>
<input
className="form-control"
type="date"
id="dateOfBirth"
name="dateOfBirth"
value={formData.dateOfBirth}
onChange={handleChange}
/>
</div>
</div>
<div className="col-sm-6 col-md-4">
<div className="form-group">
<label className="required-field" htmlFor="territory">
Territory
</label>
<select
className="form-control"
id="territory"
name="territory"
value={formData.territory}
onChange={handleChange}
required
>
<option value="">Select a Territory</option>
<option value="-1">No Territory</option>
</select>
</div>
</div>
<div className="col-sm-6 col-md-4">
<div className="form-group">
<label className="required-field" htmlFor="owner">
Owner
</label>
<select
className="form-control"
id="owner"
name="owner"
value={formData.owner}
onChange={handleChange}
required
>
<option value="">Select an Owner</option>
<option value="1">Employee 1</option>
<option value="2">Employee 2</option>
</select>
</div>
</div>
<div className="col-sm-6 col-md-4">
<div className="form-group">
<label className="required-field" htmlFor="privacySetting">
Privacy Setting
</label>
<select
className="form-control"
id="privacySetting"
name="privacySetting"
value={formData.privacySetting}
onChange={handleChange}
required
>
<option value="PublicReadWrite">Public read/write</option>
<option value="PublicReadOnly">Public read only</option>
<option value="Private">Private</option>
</select>
</div>
</div>
</div>
<div className="form-group">
<label htmlFor="about">About</label>
<input
className="form-control"
type="text"
id="about"
name="about"
value={formData.about}
onChange={handleChange}
placeholder="Enter details about the contact"
/>
</div>
<div className="form-group">
<label htmlFor="tags">Tags</label>
<input
className="form-control"
type="text"
id="tags"
name="tags"
value={formData.tags}
onChange={handleChange}
/>
<span className="form-control-label">
Press <code>Enter</code> after each tag to complete it.
</span>
</div>
</div>
<hr />
{/* Address Section */}
<div className="form-group">
<div className="header">
<h5 className="heading text-uppercase">
<strong>Address</strong>
</h5>
</div>
<div className="row">
<div className="col-sm-6">
<div className="form-group">
<span className="input-counter">250</span>
<label htmlFor="street">Address 1</label>
<input
className="form-control"
type="text"
id="street"
name="street"
value={formData.street || ""}
onChange={handleChange}
placeholder="Enter street address or P.O. box"
/>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<span className="input-counter">250</span>
<label htmlFor="streetLineTwo">Address 2</label>
<input
className="form-control"
type="text"
id="streetLineTwo"
name="streetLineTwo"
value={formData.streetLineTwo || ""}
onChange={handleChange}
placeholder="Enter apt/unit/suite number (ex. Unit 1)"
/>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-6">
<div className="form-group">
<span className="input-counter">100</span>
<label htmlFor="city">City</label>
<input
className="form-control"
type="text"
id="city"
name="city"
value={formData.city || ""}
onChange={handleChange}
placeholder="Enter city name"
/>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<span className="input-counter">20</span>
<label htmlFor="zip">Zip/Postal Code</label>
<input
className="form-control"
type="text"
id="zip"
name="zip"
value={formData.zip || ""}
onChange={handleChange}
placeholder="Enter code"
/>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-4">
<div className="form-group">
<label htmlFor="country">Country</label>
<select
className="form-control"
id="country"
name="country"
value={formData.country || "US"}
onChange={handleChange}
>
<option value="">Select a Country</option>
<option value="US">United States</option>
</select>
</div>
</div>
<div className="col-sm-4">
<div className="form-group">
<label htmlFor="state">State / Province</label>
<select
className="form-control"
id="state"
name="state"
value={formData.state || ""}
onChange={handleChange}
>
<option value="">Select a State/Province</option>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AZ">Arizona</option>
<option value="CA">California</option>
<option value="FL">Florida</option>
<option value="NY">New York</option>
<option value="TX">Texas</option>
</select>
</div>
</div>
<div className="col-sm-4">
<div className="form-group">
<label htmlFor="county">County</label>
<select
className="form-control"
id="county"
name="county"
value={formData.county || ""}
onChange={handleChange}
>
<option value="">Select a County</option>
</select>
</div>
</div>
</div>
</div>
<hr />
{/* Phone & Email Communication Section */}
<div className="form-group">
<div className="header">
<h5 className="heading text-uppercase">
<strong>Phone &amp; Email Communication</strong>
</h5>
</div>
<div className="form-group">
<label>Phone Number</label>
<p className="text-muted text-sm">
Add phone numbers for this contact
</p>
</div>
<div className="form-group">
<label>Email Addresses</label>
<p className="text-muted text-sm">
Add email addresses for this contact
</p>
</div>
</div>
<hr />
{/* Additional Details Section */}
<div className="form-group">
<div className="header">
<h5 className="heading text-uppercase">
<strong>Additional Details</strong>
</h5>
</div>
<div className="row">
<div className="col-sm-6 col-md-4">
<div className="form-group">
<label htmlFor="timeZone">Time Zone</label>
<select
className="form-control"
id="timeZone"
name="timeZone"
value={formData.timeZone}
onChange={handleChange}
>
<option value="">Select a time zone</option>
<option value="Eastern Standard Time">
(UTC-05:00) Eastern Time (US &amp; Canada)
</option>
<option value="Central Standard Time">
(UTC-06:00) Central Time (US &amp; Canada)
</option>
<option value="Mountain Standard Time">
(UTC-07:00) Mountain Time (US &amp; Canada)
</option>
<option value="Pacific Standard Time">
(UTC-08:00) Pacific Time (US &amp; Canada)
</option>
</select>
</div>
</div>
<div className="col-sm-6 col-md-4">
<div className="form-group">
<label htmlFor="smsAddress">SMS Address</label>
<div className="input-group">
<input
className="form-control"
type="email"
id="smsAddress"
name="smsAddress"
value={formData.smsAddress || ""}
onChange={handleChange}
placeholder="13625550100@providerdomain.com"
/>
<span className="input-group-btn">
<button
type="button"
title="Test SMS Text Message"
className="btn btn-primary"
>
<i className="fa fa-comment"></i>
</button>
</span>
</div>
<span className="form-control-label">
Do not include brackets or dashes for the number.
</span>
</div>
</div>
<div className="col-sm-6 col-md-4">
<div className="form-group">
<span className="input-counter">2048</span>
<label htmlFor="website">Website</label>
<input
className="form-control"
type="url"
id="website"
name="website"
value={formData.website || ""}
onChange={handleChange}
placeholder="http://"
/>
<span className="form-control-label">
Make sure to include the protocol (http://)
</span>
</div>
</div>
</div>
</div>
<hr />
{/* Social Media Section */}
<div className="form-group">
<div className="header">
<h5 className="heading text-uppercase">
<strong>Social Media</strong>
</h5>
</div>
<p className="text-muted text-sm">
Enter the full URL of the Social Media profile you wish to
link.
</p>
<div className="row">
<div className="col-sm-6 col-md-4">
<div className="form-group">
<label htmlFor="facebookUrl">Facebook</label>
<input
className="form-control"
type="text"
id="facebookUrl"
name="facebookUrl"
value={formData.facebookUrl || ""}
onChange={handleChange}
placeholder="Enter Facebook URL"
/>
</div>
</div>
<div className="col-sm-6 col-md-4">
<div className="form-group">
<label htmlFor="linkedInUrl">LinkedIn</label>
<input
className="form-control"
type="text"
id="linkedInUrl"
name="linkedInUrl"
value={formData.linkedInUrl || ""}
onChange={handleChange}
placeholder="Enter LinkedIn URL"
/>
</div>
</div>
<div className="col-sm-6 col-md-4">
<div className="form-group">
<label htmlFor="twitterUrl">Twitter</label>
<input
className="form-control"
type="text"
id="twitterUrl"
name="twitterUrl"
value={formData.twitterUrl || ""}
onChange={handleChange}
placeholder="Enter Twitter URL"
/>
</div>
</div>
</div>
</div>
<hr />
{/* Action Buttons */}
<div className="form-group text-right">
<span className="text-pad-right text-sm">
All fields marked with a <span className="required-field"></span> are
required.
</span>
<button
type="submit"
className="btn btn-lg btn-success"
disabled={loading}
>
{loading ? "Saving..." : isEdit ? "Update" : "Create"}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
);
};
export default ContactFormPage;