VA DVS Cybersecurity Analyst

vTech Solution
Richmond, VA
Job Summary:

use client;

import React useEffect useMemo useState from react;
import Card CardHeader CardTitle CardContent from @/components/ui/card;
import Button from @/components/ui/button;
import Input from @/components/ui/input;
import Badge from @/components/ui/badge;
import
GripVertical
Plus
Download
Trash2
RefreshCw
Edit3
Check
X
Save
from lucide-react;
import Textarea from @/components/ui/textarea;
import saveAs from file-saver;

const APIBASE ;

// Types
export type OutlineNode
id: string;
title: string;
description: string;
children: OutlineNode;
;

type BackendSubsection
subname: string;
subdescription: string;
;

type BackendSection
sectionname: string;
sectiondescription: string;
subsections: BackendSubsection;
;

type BackendOutline
opportunityname: string;
submittedtoname: string;
submittedtoemail: string;
submittedtophone: string;
submittedbyemail: string;
submittedbyaddress: string;
coverletterdate: string;
coverletterclientcontact: string;
coverlettersubjectline: string;
coverletterbody: string;
proposaltype: string;
sections: BackendSection;
tableofcontents: string;
k: string: any;
;

export type OutlineBuilderProps
proposalId: string;
onGenerate: (outlineText: string) > void;
isLoading: boolean;
;

// Generic helpers
const cloneSafe (v: T): T >
try
return ((v)) as T;
catch
return v;


;

const findAndRemoveNode (tree: OutlineNode id: string): OutlineNode null OutlineNode >
let removed: OutlineNode null null;
const walk (list: OutlineNode): OutlineNode >
let changed false;
const out: OutlineNode ;
for (let i 0; i < ; i)
const n listi;
if ( id)
removed n;
changed true;
continue;


if (() && )
const newChildren walk();
if (newChildren ! )
changed true;
( ...n children: newChildren );
else
(n);


else
(n);




return changed out : list;
;
const newTree walk(tree);
return removed newTree;
;

const insertNodeAt (tree: OutlineNode nodeToInsert: OutlineNode parentId: string null index: number): OutlineNode >
if (parentId null)
const safeIndex (0 ( index));
return ...(0 safeIndex) nodeToInsert ...(safeIndex);

let inserted false;
const walk (list: OutlineNode): OutlineNode >
let changed false;
const out: OutlineNode ;
for (let i 0; i < ; i)
const n listi;
if ( parentId)
const children () ... : ;
const safe (0 ( index));
(safe 0 nodeToInsert);
inserted true;
changed true;
( ...n children );
continue;


if (() && )
const nchild walk();
if (nchild ! )
changed true;
( ...n children: nchild );
else
(n);


else
(n);




return changed out : list;
;
const newTree walk(tree);
return inserted newTree : tree;
;

const findNode (tree: OutlineNode id: string): OutlineNode null >
const walk (list: OutlineNode): OutlineNode null >
for (let i 0; i < ; i)
const n listi;
if ( id) return n;
if (() && )
const f walk();
if (f) return f;




return null;
;
return walk(tree);
;

const numberOutline (nodes: OutlineNode null prefix: number ): no: string; title: string >
const out: no: string; title: string ;
const arr (nodes) nodes : ;
((n idx) >
const no ...prefix idx (.);
( no title: );
if ()
const sub numberOutline( ...prefix idx 1);
(...sub);


);
return out;
;

const outlineToText (nodes: OutlineNode null): string >
numberOutline(nodes).map((x) > $ $).join(n);

const OutlineBuilder: ( proposalId onGenerate isLoading ) >
const nodes setNodes useState();
const outlineJson setOutlineJson useState(null);
const loadingOutline setLoadingOutline useState(true);
const saving setSaving useState(false);
const deleting setDeleting useState(false);
const draggedId setDraggedId useState(null);
const dropHint setDropHint useState(null);
const selectedTemplate setSelectedTemplate useState();

const meta setMeta useState(
opportunityname:
submittedtoname:
submittedtoemail:
submittedtophone:
coverletterdate:
coverletterclientcontact:
coverlettersubjectline:
coverletterbody:
);

const mapBackendToNodes (outline: BackendOutline null): OutlineNode >
if (!outline) return ;
const sections () : ;
return ((s idx) > (
id: ()
title: Section $idx 1
description:
children: ()
((sub j) > (
id: ()
title: Subsection $j 1
description:
children:
))
:
));
;

const mapNodesToBackendSections (list: OutlineNode): BackendSection >
return ((list) list : ).map((n) > (
sectionname:
sectiondescription:
subsections: ()
((c) > (
subname:
subdescription:
))
:
));
;

const fetchOutline async () >
if (!proposalId !APIBASE) return;
try
setLoadingOutline(true);
const res await fetch($APIBASE/api/proposals/$proposalId/outline method: GET );
if (!)
const txt await ().catch(() > );
throw new Error(GET failed: $ $txt);


const data await ();
const outline: BackendOutline data;
setOutlineJson(outline);
setMeta(
opportunityname:
submittedtoname:
submittedtoemail:
submittedtophone:
coverletterdate:
coverletterclientcontact:
coverlettersubjectline:
coverletterbody:
);
const mapped mapBackendToNodes(outline);
setNodes(mapped);
catch (err)
(fetchOutline error: err);
alert(Failed to load outline.);
finally
setLoadingOutline(false);


;

useEffect(() >
fetchOutline();
// eslint-disable-next-line react-hooks/exhaustive-deps
proposalId);

const outlineText useMemo(() > outlineToText(nodes) nodes);

const removeNodeById (id: string) >
const newTree findAndRemoveNode(nodes as any id);
setNodes(newTree as any);
;

const updateTitle (id: string text: string) >
const node findNode(nodes as any id);
if (node)
text;
setNodes(...nodes);


;

const updateDescription (id: string desc: string) >
const node findNode(nodes id);
if (node)
const updatedNode ...node description: desc ;
const newNodes ((n) > ( id updatedNode : n));
setNodes(newNodes);


;

const addChild (parentId: string) >
const newChild: OutlineNode
id: ()
title: New Subsection
description:
children:
;
const parentNode findNode(nodes as any parentId);
const newIndex 0;
const newTree insertNodeAt(nodes as any newChild as any parentId newIndex);
if (newTree ! nodes) setNodes(newTree as any);
;

const addRoot () >
setNodes(...nodes id: () title: New Section description: );
;

const onDragStart (e: id: string) >
setDraggedId(id);
(text/plain id);
;

const onDragOverGeneric (e: ) >
();
;

const endDragCleanup () >
setDraggedId(null);
setDropHint(null);
;

useEffect(() >
const cleanup () > endDragCleanup();
(dragend cleanup);
return () > (dragend cleanup);
);

const findParentAndIndex (tree: OutlineNode id: string): parentId: string null; index: number null >
const walk (arr: OutlineNode parentId: string null): parentId: string null; index: number null >
for (let i 0; i < ; i)
const n arri;
if ( id) return parentId index: i ;
if ()
const found walk( );
if (found) return found;




return null;
;
return walk(tree null);
;

const handleDragLeaveRoot (e: ) >
const to as HTMLElement null;
if (!to !(.outline-left-column))
setDropHint(null);


;

const onDropBetween (e: parentId: string null index: number) >
();
let dragged (text/plain);
if (!dragged && draggedId) dragged draggedId;
if (!dragged) return endDragCleanup();

const origin findParentAndIndex(nodes dragged);
const originParent null;
const originIndex -1;
let targetIndex index;
if (originParent parentId && originIndex < index) targetIndex index - 1;
if (originParent parentId && targetIndex originIndex) return endDragCleanup();

const removed afterRemove findAndRemoveNode(nodes as any dragged);
if (!removed) return endDragCleanup();

const newTree insertNodeAt(afterRemove as any removed as any parentId targetIndex);
if (newTree ! nodes) setNodes(newTree as any);
endDragCleanup();
;

const onDropInto (e: parentId: string null) >
();
let dragged (text/plain);
if (!dragged && draggedId) dragged draggedId;
if (!dragged) return endDragCleanup();

if (parentId && isDescendant(nodes dragged parentId)) return endDragCleanup();

const origin findParentAndIndex(nodes dragged);
const originParent null;
const originIndex -1;
const removed afterRemove findAndRemoveNode(nodes as any dragged);
if (!removed) return endDragCleanup();

const parentNode parentId findNode(afterRemove as any parentId) : null;
let newIndex ;
if (originParent parentId && originIndex < newIndex) newIndex (0 newIndex - 1);

const newTree insertNodeAt(afterRemove as any removed as any parentId newIndex);
if (newTree ! nodes) setNodes(newTree as any);
endDragCleanup();
;

const TitleInline: ( id value ) >
const editing setEditing useState(false);
const val setVal useState(value);

useEffect(() > setVal(value) value);

return (


editing (


setVal() />


updateTitle(id val value);
setEditing(false);


>






setVal(value);
setEditing(false);


>






) : (


value
setEditing(true)>






)

);
;

return (






Proposal Outline


Type: $ :







Reload




saving (


Saving


) : (


Save Outline


)


onGenerate(outlineText) disabled!>
Generate Draft


downloadDocx(selectedTemplate) disabled!>
Download DOCX




Delete Outline













> 0 (


) : (

No sections found. Add or reload.

)




Add Top-Level Section











Cover & Metadata




Opportunity Name


setMeta( ...meta opportunityname: )


/>


Submitted To (Name)


setMeta( ...meta submittedtoname: )


/>




Email


setMeta( ...meta submittedtoemail: )


/>


Phone


setMeta( ...meta submittedtophone: )


/>


Cover Letter Date


setMeta( ...meta coverletterdate: )


/>


Client Contact


setMeta(
...meta
coverletterclientcontact:
)


/>


Subject Line


setMeta(
...meta
coverlettersubjectline:
)


/>


Cover Letter Body


setMeta(
...meta
coverletterbody:
)


/>





Template


Choose Template
setSelectedTemplate()
>









Tip: Edit outline on left. Edit cover details here. Save to Firestore or Export DOCX anytime.











);
;

export default OutlineBuilder;

Location:Richmond Virginia United States

Responsibilities:


- Support the development and scoping of the risk assessment plan.
- Identify assess and document applicable security controls.
- Collaborate with stakeholders to collect data and evidence.
- Perform detailed analysis of access control measures.
- Review security documentation and system procedures.
- Prepare draft and final risk assessment reports.
- Collect and review artifacts demonstrating compliance with applicable security controls.
- Incorporate feedback and deliver a final report.

Required Skills & Certifications:


- Bachelors degree in Cybersecurity Information Technology Computer Science or related field.
- Minimum 5 years of experience in cybersecurity risk assessment or compliance projects.
- Strong understanding of NIST 800-53 and SEC530 Information Security Standards.
- Hands-on experience with access control evaluations and documentation of compliance evidence.
- Excellent analytical reporting and communication skills.

Preferred Skills & Certifications:


- Certifications such as CISSP CISA CISM CRISC or Security.
- Experience working with state or federal agencies or similar regulatory environments.

Special Considerations:


- N/A

Scheduling:
- N/A

Posted 2025-11-21

Recommended Jobs

Senior HVAC Technician

Wisler Plumbing, Heating, Cooling and Electric
Rocky Mount, VA

Join Our Team as a Senior HVAC Technician At Wisler Plumbing, Heating, Cooling and Electric, we don’t just hire techs—we build careers rooted in respect, growth, and purpose. If you’re a seasoned …

View Details
Posted 2025-11-21

Client Service Representative (Moneta Branch)

Atlantic Union Bank Careers
Bedford County, VA

The position is an all-encompassing role which requires the teammate to deliver best in class client experience sales/service solutions as well as handle teller transactions. The Client Services Repr…

View Details
Posted 2025-11-22

Senior Associate, CRE Loan Screening

Capital One
McLean, VA

Senior Associate, CRE Loan Screening The Senior Associate, CRE Loan Screening role will sit on a team of screeners and analysts to pre-qualify and structure Commercial Real Estate (CRE) Balance Sheet …

View Details
Posted 2025-11-19

Server Assistant

The Capital Grille
Fairfax, VA

We Wine. We Dine. We Build Careers!  For this position, pay will be variable by location - plus tips.   We cordially invite you to apply. Joining our team provides exceptionally distinct…

View Details
Posted 2025-07-25

Construction Inspector

Mead & Hunt
Virginia

Responsibilities: Mead & Hunt is an employee-owned, flexible company that believes in giving back to our communities, investing in our employees, and building relationships that last. We value taking…

View Details
Posted 2025-11-13

Technical Service Representative - VA Area

AkzoNobel
Roanoke, VA

About AkzoNobel Since 1792, we’ve been supplying the innovative paints and coatings that help to color people’s lives and protect what matters most. Our world class portfolio of brands – including…

View Details
Posted 2025-09-06

Electrical Design Engineer

Pkaza
Ashburn, VA

Electrical Design Engineer - Ashburn, VA  Our client is a global provider and manufacturer of industrial power equipment used in emergency back up power applications for mission critical operations…

View Details
Posted 2025-11-06

Lead Mechanic

City of Fredericksburg
Fredericksburg, VA

​ ​ VACANCY ANNOUNCEMENT Lead Mechanic Starting Salary Range: $50,896 - $55,985​ Job Summary: Join our team as a skilled Wastewater Treatment Plant Lead Mechanic. This position involves main…

View Details
Posted 2025-11-07

US Tech - Technical Business Analyst Manager Save for Later Remove job

PwC
Richmond, VA

At PwC, our people in software and product innovation focus on developing cutting-edge software solutions and driving product innovation to meet the evolving needs of clients. These individuals com…

View Details
Posted 2025-09-09