datastake-daf 0.6.251 → 0.6.252

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15422,226 +15422,6 @@ const PdfForm = _ref3 => {
15422
15422
  return organizedSections;
15423
15423
  };
15424
15424
  const organizedForm = React.useMemo(() => organizeFormByHeaders(form), [form]);
15425
-
15426
- // Constants for height calculations (same as PdfView)
15427
- const PAGE_HEIGHT = 1587;
15428
- const FOOTER_HEIGHT = 70;
15429
- const HEADER_HEIGHT = 100;
15430
-
15431
- // Helper function to estimate tree node height based on content and type
15432
- const estimateTreeNodeHeight = function (key, config, value) {
15433
- let level = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
15434
- const baseHeight = 28; // Base height per tree node
15435
- const indentHeight = level * 2; // Additional height per level
15436
- let totalHeight = baseHeight + indentHeight;
15437
-
15438
- // Adjust height based on node type
15439
- if ((config === null || config === void 0 ? void 0 : config.type) === 'header') {
15440
- totalHeight += 15; // Headers need more space
15441
- } else if ((config === null || config === void 0 ? void 0 : config.type) === 'textarea') {
15442
- totalHeight += 40; // Textareas are taller
15443
- } else if ((config === null || config === void 0 ? void 0 : config.type) === 'dataLink' || (config === null || config === void 0 ? void 0 : config.type) === 'dataLinkGroup') {
15444
- totalHeight += 20; // Data links often have more content
15445
- }
15446
-
15447
- // Add height for value content if it exists
15448
- if (value && typeof value === 'string' && value.length > 50) {
15449
- totalHeight += Math.ceil(value.length / 50) * 15; // Multi-line content
15450
- }
15451
-
15452
- // Add height for children recursively
15453
- if (config !== null && config !== void 0 && config.inputs) {
15454
- const childKeys = Object.keys(config.inputs).filter(childKey => {
15455
- const childConfig = config.inputs[childKey];
15456
- // Check showIf condition
15457
- if (childConfig !== null && childConfig !== void 0 && childConfig.showIf && !evaluateShowIfCondition(childConfig.showIf, data)) {
15458
- return false;
15459
- }
15460
- return true;
15461
- }).sort((a, b) => {
15462
- var _config$inputs$a3, _config$inputs$b3;
15463
- const positionA = ((_config$inputs$a3 = config.inputs[a]) === null || _config$inputs$a3 === void 0 ? void 0 : _config$inputs$a3.position) || 0;
15464
- const positionB = ((_config$inputs$b3 = config.inputs[b]) === null || _config$inputs$b3 === void 0 ? void 0 : _config$inputs$b3.position) || 0;
15465
- return positionA - positionB;
15466
- });
15467
- childKeys.forEach(childKey => {
15468
- const childConfig = config.inputs[childKey];
15469
- const childValue = (value === null || value === void 0 ? void 0 : value[childKey]) || (data === null || data === void 0 ? void 0 : data[childKey]);
15470
- totalHeight += estimateTreeNodeHeight(childKey, childConfig, childValue, level + 1);
15471
- });
15472
- }
15473
-
15474
- // Handle array/repeated content
15475
- if (Array.isArray(value)) {
15476
- value.forEach(itemValue => {
15477
- totalHeight += estimateTreeNodeHeight(key, config, itemValue, level);
15478
- });
15479
- }
15480
- return totalHeight;
15481
- };
15482
-
15483
- // Helper function to split section based on height constraints (goes deep if needed)
15484
- const createHeightConstrainedSections = (sectionKey, section) => {
15485
- const MAX_SECTION_HEIGHT = PAGE_HEIGHT - HEADER_HEIGHT - FOOTER_HEIGHT - 150; // Conservative buffer
15486
- const subSections = [];
15487
-
15488
- // Get all top-level items in the section with detailed analysis
15489
- const topLevelItems = Object.keys(section).filter(key => !(key === 'id' || key === 'label' || key === 'position' || key === 'subTitle')).map(key => {
15490
- var _section$key;
15491
- return {
15492
- key,
15493
- config: section[key],
15494
- estimatedHeight: estimateTreeNodeHeight(key, section[key], data === null || data === void 0 ? void 0 : data[key]),
15495
- canSplit: ((_section$key = section[key]) === null || _section$key === void 0 ? void 0 : _section$key.inputs) && Object.keys(section[key].inputs).length > 1 // Can this item be further split?
15496
- };
15497
- }).sort((a, b) => {
15498
- var _a$config, _b$config;
15499
- return (((_a$config = a.config) === null || _a$config === void 0 ? void 0 : _a$config.position) || 0) - (((_b$config = b.config) === null || _b$config === void 0 ? void 0 : _b$config.position) || 0);
15500
- });
15501
- let currentSubSection = _objectSpread2({}, section);
15502
-
15503
- // Remove all items from the base section structure
15504
- Object.keys(section).forEach(key => {
15505
- if (!(key === 'id' || key === 'label' || key === 'position' || key === 'subTitle')) {
15506
- delete currentSubSection[key];
15507
- }
15508
- });
15509
- let currentHeight = 80; // Base height for section header
15510
- let subSectionIndex = 0;
15511
- topLevelItems.forEach((item, index) => {
15512
- const {
15513
- key,
15514
- config,
15515
- estimatedHeight,
15516
- canSplit
15517
- } = item;
15518
-
15519
- // If a single item is too large and can be split, split it at the child level
15520
- if (estimatedHeight > MAX_SECTION_HEIGHT * 0.8 && canSplit) {
15521
- // Split this large item into smaller parts
15522
- const childSplits = splitLargeItem(key, config, data === null || data === void 0 ? void 0 : data[key], MAX_SECTION_HEIGHT * 0.6);
15523
- childSplits.forEach((splitItem, splitIndex) => {
15524
- // Check if current subsection has room
15525
- if (currentHeight + splitItem.estimatedHeight > MAX_SECTION_HEIGHT && Object.keys(currentSubSection).length > 4) {
15526
- // Save current sub-section
15527
- subSections.push({
15528
- key: "".concat(sectionKey, "_part_").concat(subSectionIndex),
15529
- section: _objectSpread2({}, currentSubSection),
15530
- title: section.label
15531
- });
15532
-
15533
- // Start new sub-section
15534
- currentSubSection = {
15535
- id: section.id,
15536
- label: section.label,
15537
- position: section.position + subSectionIndex + 1,
15538
- subTitle: section.subTitle
15539
- };
15540
- currentHeight = 80;
15541
- subSectionIndex++;
15542
- }
15543
-
15544
- // Add split item to current sub-section
15545
- currentSubSection[splitItem.key] = splitItem.config;
15546
- currentHeight += splitItem.estimatedHeight;
15547
- });
15548
- } else {
15549
- // Regular processing for items that fit or can't be split
15550
- // Check if adding this item would exceed height limit
15551
- if (currentHeight + estimatedHeight > MAX_SECTION_HEIGHT && Object.keys(currentSubSection).length > 4) {
15552
- // Save current sub-section
15553
- subSections.push({
15554
- key: "".concat(sectionKey, "_part_").concat(subSectionIndex),
15555
- section: _objectSpread2({}, currentSubSection),
15556
- title: section.label
15557
- });
15558
-
15559
- // Start new sub-section
15560
- currentSubSection = {
15561
- id: section.id,
15562
- label: section.label,
15563
- position: section.position + subSectionIndex + 1,
15564
- subTitle: section.subTitle
15565
- };
15566
- currentHeight = 80;
15567
- subSectionIndex++;
15568
- }
15569
-
15570
- // Add item to current sub-section
15571
- currentSubSection[key] = config;
15572
- currentHeight += estimatedHeight;
15573
- }
15574
- });
15575
-
15576
- // Add the final sub-section if it has content
15577
- if (Object.keys(currentSubSection).length > 4) {
15578
- subSections.push({
15579
- key: subSectionIndex === 0 ? sectionKey : "".concat(sectionKey, "_part_").concat(subSectionIndex),
15580
- section: currentSubSection,
15581
- title: section.label
15582
- });
15583
- }
15584
- return subSections.length > 0 ? subSections : [{
15585
- key: sectionKey,
15586
- section: section,
15587
- title: section.label
15588
- }];
15589
- };
15590
-
15591
- // Helper function to split large items at the child level
15592
- const splitLargeItem = (parentKey, parentConfig, parentValue, maxHeight) => {
15593
- if (!(parentConfig !== null && parentConfig !== void 0 && parentConfig.inputs)) {
15594
- return [{
15595
- key: parentKey,
15596
- config: parentConfig,
15597
- estimatedHeight: estimateTreeNodeHeight(parentKey, parentConfig, parentValue)
15598
- }];
15599
- }
15600
- const childItems = Object.keys(parentConfig.inputs).filter(childKey => {
15601
- const childConfig = parentConfig.inputs[childKey];
15602
- return !(childConfig !== null && childConfig !== void 0 && childConfig.showIf) || evaluateShowIfCondition(childConfig.showIf, data);
15603
- }).map(childKey => ({
15604
- key: childKey,
15605
- config: parentConfig.inputs[childKey],
15606
- estimatedHeight: estimateTreeNodeHeight(childKey, parentConfig.inputs[childKey], parentValue === null || parentValue === void 0 ? void 0 : parentValue[childKey])
15607
- })).sort((a, b) => {
15608
- var _a$config2, _b$config2;
15609
- return (((_a$config2 = a.config) === null || _a$config2 === void 0 ? void 0 : _a$config2.position) || 0) - (((_b$config2 = b.config) === null || _b$config2 === void 0 ? void 0 : _b$config2.position) || 0);
15610
- });
15611
- const splits = [];
15612
- let currentSplit = {
15613
- key: "".concat(parentKey, "_part_0"),
15614
- config: _objectSpread2(_objectSpread2({}, parentConfig), {}, {
15615
- inputs: {}
15616
- }),
15617
- estimatedHeight: 40 // Base height for parent structure
15618
- };
15619
- let splitIndex = 0;
15620
- childItems.forEach(child => {
15621
- if (currentSplit.estimatedHeight + child.estimatedHeight > maxHeight && Object.keys(currentSplit.config.inputs).length > 0) {
15622
- splits.push(currentSplit);
15623
- splitIndex++;
15624
- currentSplit = {
15625
- key: "".concat(parentKey, "_part_").concat(splitIndex),
15626
- config: _objectSpread2(_objectSpread2({}, parentConfig), {}, {
15627
- label: parentConfig.label,
15628
- inputs: {}
15629
- }),
15630
- estimatedHeight: 40
15631
- };
15632
- }
15633
- currentSplit.config.inputs[child.key] = child.config;
15634
- currentSplit.estimatedHeight += child.estimatedHeight;
15635
- });
15636
- if (Object.keys(currentSplit.config.inputs).length > 0) {
15637
- splits.push(currentSplit);
15638
- }
15639
- return splits.length > 0 ? splits : [{
15640
- key: parentKey,
15641
- config: parentConfig,
15642
- estimatedHeight: estimateTreeNodeHeight(parentKey, parentConfig, parentValue)
15643
- }];
15644
- };
15645
15425
  const pdfConfig = React.useMemo(() => {
15646
15426
  const sections = [];
15647
15427
  Object.keys(organizedForm).forEach(sectionKey => {
@@ -15649,40 +15429,28 @@ const PdfForm = _ref3 => {
15649
15429
  if (typeof section !== 'object' || !section.label) {
15650
15430
  return;
15651
15431
  }
15652
-
15653
- // Create height-constrained sub-sections
15654
- const subSections = createHeightConstrainedSections(sectionKey, section);
15655
- subSections.forEach(_ref4 => {
15656
- let {
15657
- key,
15658
- section: subSection,
15659
- title
15660
- } = _ref4;
15661
- sections.push({
15662
- render: () => /*#__PURE__*/jsxRuntime.jsx("div", {
15663
- className: "pdf-form-section",
15664
- children: /*#__PURE__*/jsxRuntime.jsx(PdfFormContent, {
15665
- form: {
15666
- [key]: _objectSpread2(_objectSpread2({}, subSection), {}, {
15667
- label: title
15668
- })
15669
- },
15670
- data: data,
15671
- t: t,
15672
- user: user,
15673
- title: formName,
15674
- source: source,
15675
- version: version,
15676
- getApiBaseUrl: getApiBaseUrl,
15677
- getAppHeader: getAppHeader,
15678
- app: app
15679
- })
15680
- }, key),
15681
- style: {
15682
- marginBottom: '20px',
15683
- padding: '0 20px'
15684
- }
15685
- });
15432
+ sections.push({
15433
+ render: () => /*#__PURE__*/jsxRuntime.jsx("div", {
15434
+ className: "pdf-form-section",
15435
+ children: /*#__PURE__*/jsxRuntime.jsx(PdfFormContent, {
15436
+ form: {
15437
+ [sectionKey]: section
15438
+ },
15439
+ data: data,
15440
+ t: t,
15441
+ user: user,
15442
+ title: formName,
15443
+ source: source,
15444
+ version: version,
15445
+ getApiBaseUrl: getApiBaseUrl,
15446
+ getAppHeader: getAppHeader,
15447
+ app: app
15448
+ })
15449
+ }, sectionKey),
15450
+ style: {
15451
+ marginBottom: '20px',
15452
+ padding: '0 20px'
15453
+ }
15686
15454
  });
15687
15455
  });
15688
15456
  return sections;
@@ -18640,7 +18408,7 @@ const useMap$1 = ({
18640
18408
  const pos = allData.map(m => Array.isArray(m.area) ? m.area : [Number(m.marker?.lat ?? 0), Number(m.marker?.lng ?? 0)]);
18641
18409
  const bounds = new L__namespace.LatLngBounds(pos);
18642
18410
  mapRef.fitBounds(bounds, {
18643
- padding: [20, 20]
18411
+ padding: [10, 10]
18644
18412
  });
18645
18413
  }
18646
18414
  }
@@ -19931,6 +19699,39 @@ const useGlobe = ({
19931
19699
  }));
19932
19700
  roots.current.push(root);
19933
19701
  }
19702
+ } else if (type === "project") {
19703
+ // Handle project markers
19704
+ const el = document.createElement('div');
19705
+ el.className = 'mapboxgl-marker project-marker';
19706
+ el.style.width = '30px';
19707
+ el.style.height = '30px';
19708
+ el.style.backgroundColor = '#52c41a'; // Green color for projects
19709
+ el.style.borderRadius = '50%';
19710
+ el.style.border = '3px solid white';
19711
+ el.style.cursor = 'pointer';
19712
+ el.style.boxShadow = '0px 3.45px 3.45px 0px #00000029';
19713
+ el.style.display = 'flex';
19714
+ el.style.alignItems = 'center';
19715
+ el.style.justifyContent = 'center';
19716
+ el.style.color = 'white';
19717
+ el.style.fontWeight = 'bold';
19718
+ el.style.fontSize = '12px';
19719
+
19720
+ // Add project icon (you can customize this)
19721
+ el.innerHTML = '📋';
19722
+ const div = document.createElement("div");
19723
+ const root = client.createRoot(div);
19724
+ root.render( /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
19725
+ children: renderTooltipJsx({
19726
+ title: d.name,
19727
+ items: renderTooltip(d),
19728
+ link,
19729
+ total: d.sources,
19730
+ onClickLink: () => onClickLink(d)
19731
+ })
19732
+ }));
19733
+ roots.current.push(root);
19734
+ marker = new mapboxgl.Marker(el).setLngLat([d.marker.lng, d.marker.lat]).setPopup(new mapboxgl.Popup().setDOMContent(div)).addTo(mapRef);
19934
19735
  } else {
19935
19736
  // Default marker for other types
19936
19737
  const el = document.createElement('div');
@@ -20737,29 +20538,12 @@ const DAFSteps = _ref => {
20737
20538
  let {
20738
20539
  direction = "vertical",
20739
20540
  current = 0,
20740
- items = [],
20741
- scrollThreshold = 4
20541
+ items = []
20742
20542
  } = _ref;
20743
- const shouldScroll = items.length > scrollThreshold;
20744
- const scrollableStyles = shouldScroll ? _objectSpread2(_objectSpread2({}, direction === "vertical" ? {
20745
- maxHeight: "300px",
20746
- overflowY: "auto",
20747
- overflowX: "hidden"
20748
- } : {
20749
- maxWidth: "100%",
20750
- overflowX: "auto",
20751
- overflowY: "hidden",
20752
- whiteSpace: "nowrap"
20753
- }), {}, {
20754
- padding: "8px",
20755
- marginRight: direction === "vertical" ? "-8px" : "0",
20756
- paddingRight: direction === "vertical" ? "16px" : "8px"
20757
- }) : {};
20758
- return /*#__PURE__*/jsxRuntime.jsx(Widget, {
20759
- title: "Life Cycle",
20760
- className: "with-border-header",
20761
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
20762
- style: scrollableStyles,
20543
+ return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
20544
+ children: /*#__PURE__*/jsxRuntime.jsx(Widget, {
20545
+ title: "Life Cycle",
20546
+ className: "with-border-header",
20763
20547
  children: /*#__PURE__*/jsxRuntime.jsx(antd.Steps, {
20764
20548
  direction: direction,
20765
20549
  current: current,
@@ -20774,14 +20558,12 @@ DAFSteps.propTypes = {
20774
20558
  items: PropTypes__default["default"].arrayOf(PropTypes__default["default"].shape({
20775
20559
  title: PropTypes__default["default"].string.isRequired,
20776
20560
  description: PropTypes__default["default"].string
20777
- })),
20778
- scrollThreshold: PropTypes__default["default"].number
20561
+ }))
20779
20562
  };
20780
20563
  DAFSteps.defaultProps = {
20781
20564
  direction: "vertical",
20782
20565
  current: 0,
20783
- items: [],
20784
- scrollThreshold: 4
20566
+ items: []
20785
20567
  };
20786
20568
 
20787
20569
  /**
@@ -3835,7 +3835,7 @@ function showHideForm(form, formsValue) {
3835
3835
 
3836
3836
  /* eslint-disable no-constant-condition */
3837
3837
  const getNkey = namespace => {
3838
- if (['location', 'scl', 'village', 'event', 'incidents', 'corrective-actions', 'testimonials', 'initiatives', 'victims', 'pictures', 'documents', 'lir', 'sp', 'im', 'sci', 'bpe', 'gm'].includes(namespace)) {
3838
+ if (['location', 'scl', 'village', 'event', 'incidents', 'corrective-actions', 'testimonials', 'initiatives', 'victims', 'pictures', 'documents', 'lir', 'sp', 'im', 'sci', 'bpe', 'gm', 'project-readiness'].includes(namespace)) {
3839
3839
  return 'scoping';
3840
3840
  }
3841
3841
  return namespace;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datastake-daf",
3
- "version": "0.6.251",
3
+ "version": "0.6.252",
4
4
  "dependencies": {
5
5
  "@ant-design/icons": "^5.2.5",
6
6
  "@antv/g2": "^5.1.1",
@@ -64,3 +64,11 @@ export const LocationGlobe = {
64
64
  type: "location",
65
65
  },
66
66
  };
67
+
68
+ export const ProjectGlobe = {
69
+ name: "Project Globe",
70
+ args: {
71
+ ...configs.ProjectConfig,
72
+ type: "project",
73
+ },
74
+ };
@@ -264,6 +264,48 @@ export const useGlobe = ({
264
264
 
265
265
  roots.current.push(root);
266
266
  }
267
+ } else if (type === "project") {
268
+ // Handle project markers
269
+ const el = document.createElement('div');
270
+ el.className = 'mapboxgl-marker project-marker';
271
+ el.style.width = '30px';
272
+ el.style.height = '30px';
273
+ el.style.backgroundColor = '#52c41a'; // Green color for projects
274
+ el.style.borderRadius = '50%';
275
+ el.style.border = '3px solid white';
276
+ el.style.cursor = 'pointer';
277
+ el.style.boxShadow = '0px 3.45px 3.45px 0px #00000029';
278
+ el.style.display = 'flex';
279
+ el.style.alignItems = 'center';
280
+ el.style.justifyContent = 'center';
281
+ el.style.color = 'white';
282
+ el.style.fontWeight = 'bold';
283
+ el.style.fontSize = '12px';
284
+
285
+ // Add project icon (you can customize this)
286
+ el.innerHTML = '📋';
287
+
288
+ const div = document.createElement("div");
289
+ const root = createRoot(div);
290
+
291
+ root.render(
292
+ <>
293
+ {renderTooltipJsx({
294
+ title: d.name,
295
+ items: renderTooltip(d),
296
+ link,
297
+ total: d.sources,
298
+ onClickLink: () => onClickLink(d),
299
+ })}
300
+ </>,
301
+ );
302
+
303
+ roots.current.push(root);
304
+
305
+ marker = new mapboxgl.Marker(el)
306
+ .setLngLat([d.marker.lng, d.marker.lat])
307
+ .setPopup(new mapboxgl.Popup().setDOMContent(div))
308
+ .addTo(mapRef);
267
309
  } else {
268
310
  // Default marker for other types
269
311
  const el = document.createElement('div');
@@ -306,7 +306,7 @@ export const useMap = ({
306
306
  : [Number(m.marker?.lat ?? 0), Number(m.marker?.lng ?? 0)],
307
307
  );
308
308
  const bounds = new L.LatLngBounds(pos);
309
- mapRef.fitBounds(bounds, { padding: [20, 20] });
309
+ mapRef.fitBounds(bounds, { padding: [10, 10] });
310
310
  }
311
311
  }
312
312
  }, [allData, mapRef]);
@@ -3,5 +3,6 @@ import { storyConfig as DefaultMapConfig } from "./storyConfig2.js";
3
3
  import { storyConfig as TerritoryMapConfig } from "./storyConfig3.js";
4
4
  import { storyConfig as StakeholderMapConfig } from "./storyConfig4.js";
5
5
  import { storyConfig as ChainMapConfig } from "./storyConfig5.js";
6
+ import { storyConfig as ProjectConfig } from "./storyConfig6.js";
6
7
 
7
- export { DefaultMapConfig, TerritoryMapConfig, StakeholderMapConfig, EventConfig, ChainMapConfig };
8
+ export { DefaultMapConfig, TerritoryMapConfig, StakeholderMapConfig, EventConfig, ChainMapConfig, ProjectConfig };
@@ -0,0 +1,366 @@
1
+ import { findCoordinatesByCountry } from "../Globe/globeHelpers";
2
+
3
+ // Transform project data to match Globe component expectations
4
+ const transformProjectsToGlobeData = (projects) => {
5
+ return projects.map(project => {
6
+ // Get coordinates from country code
7
+ const coordinates = findCoordinatesByCountry(project.country);
8
+
9
+ return {
10
+ ...project,
11
+ marker: {
12
+ lat: coordinates[0],
13
+ lng: coordinates[1]
14
+ },
15
+ name: project.name || project.title,
16
+ description: project.projectDescription || "No description available",
17
+ total: 1, // Each project is a single marker
18
+ sources: 1,
19
+ type: "project",
20
+ // Add project-specific data for tooltip
21
+ projectData: {
22
+ datastakeId: project.datastakeId,
23
+ sectoralScope: project.sectoralScope,
24
+ percentageCompletion: project.percentageCompletion,
25
+ country: project.country,
26
+ author: project.author?.name,
27
+ createdAt: project.createdAt,
28
+ mainImage: project.mainImage?.[0]?.url
29
+ }
30
+ };
31
+ });
32
+ };
33
+
34
+ // Sample project data from the user
35
+ const sampleProjects = [
36
+ {
37
+ "_id": "687a4d9a4cfa60f30db94c52",
38
+ "createdAt": "2025-07-18T00:00:00.000Z",
39
+ "updatedAt": "2025-07-18T00:00:00.000Z",
40
+ "form": "straatosProject",
41
+ "id": "3129457d-b787-4955-ba25-04d67fecfaaa",
42
+ "name": "ABC Mangrove Senegal",
43
+ "country": "SN",
44
+ "datastakeId": "PRJ-00000000103",
45
+ "title": "ABC Mangrove Senegal",
46
+ "sectoralScope": "agricultureForestryAndOtherLandUse",
47
+ "sdgs": [],
48
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
49
+ "percentageCompletion": 11.76470588235294,
50
+ "projectDescription": "ABC Mangrove Senegal is an environmental and community-focused initiative dedicated to the restoration, preservation, and sustainable management of mangrove ecosystems in Senegal.",
51
+ "mainImage": [
52
+ {
53
+ "url": "https://cdn.straatos.io/dev/company-df5378ae-704a-4a46-93b2-336cc5341456/user-e8d71443-339f-4b45-bbf5-22f0e5f09601/auth-bg_cropped-1754901830065.png",
54
+ "path": "dev/company-df5378ae-704a-4a46-93b2-336cc5341456/user-e8d71443-339f-4b45-bbf5-22f0e5f09601/auth-bg_cropped-1754901830065.png",
55
+ "name": "auth-bg_cropped-1754901830065.png",
56
+ "size": 1693409,
57
+ "type": "image/png"
58
+ }
59
+ ],
60
+ "author": {
61
+ "name": "Geri SHPK",
62
+ "country": "AL",
63
+ "category": "civilSociety",
64
+ "subCategory": "internationalNGO"
65
+ }
66
+ },
67
+ {
68
+ "_id": "687a57e24cfa60f30db954ca",
69
+ "createdAt": "2025-07-18T00:00:00.000Z",
70
+ "updatedAt": "2025-07-18T00:00:00.000Z",
71
+ "form": "project",
72
+ "id": "10e09c02-1986-4eb5-9069-355bffdfb93d",
73
+ "name": "Testing",
74
+ "country": "SN",
75
+ "datastakeId": "PRJ-00000000104",
76
+ "title": "Testing",
77
+ "sectoralScope": "agricultureForestryAndOtherLandUse",
78
+ "sdgs": [],
79
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
80
+ "percentageCompletion": 28.57142857142857,
81
+ "author": {
82
+ "name": "Geri SHPK",
83
+ "country": "AL",
84
+ "category": "civilSociety",
85
+ "subCategory": "internationalNGO"
86
+ }
87
+ },
88
+ {
89
+ "_id": "687e619d4cfa60f30db967ea",
90
+ "createdAt": "2025-07-21T15:49:49.431Z",
91
+ "updatedAt": "2025-07-21T15:49:49.431Z",
92
+ "form": "straatosProject",
93
+ "id": "51440c46-2d42-452d-8ea1-614002589d4c",
94
+ "name": "Pietra's Project",
95
+ "country": "BR",
96
+ "datastakeId": "PRJ-00000000105",
97
+ "title": "Pietra's Project",
98
+ "sectoralScope": "agricultureForestryAndOtherLandUse",
99
+ "sdgs": [],
100
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
101
+ "author": {
102
+ "name": "Geri SHPK",
103
+ "country": "AL",
104
+ "category": "civilSociety",
105
+ "subCategory": "internationalNGO"
106
+ }
107
+ },
108
+ {
109
+ "_id": "6883918729bcd5751e8dd579",
110
+ "createdAt": "2025-07-25T14:15:35.305Z",
111
+ "updatedAt": "2025-07-25T14:15:35.305Z",
112
+ "form": "straatosProject",
113
+ "id": "275acff8-c478-40a5-a23d-94968dca01d1",
114
+ "name": "efwefwe",
115
+ "country": "AX",
116
+ "datastakeId": "PRJ-00000000106",
117
+ "title": "efwefwe",
118
+ "sectoralScope": "agricultureForestryAndOtherLandUse",
119
+ "sdgs": [],
120
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
121
+ "percentageCompletion": 100,
122
+ "author": {
123
+ "name": "Geri SHPK",
124
+ "country": "AL",
125
+ "category": "civilSociety",
126
+ "subCategory": "internationalNGO"
127
+ }
128
+ },
129
+ {
130
+ "_id": "6883b50e29bcd5751e8dda3e",
131
+ "createdAt": "2025-07-25T00:00:00.000Z",
132
+ "updatedAt": "2025-07-25T00:00:00.000Z",
133
+ "form": "project",
134
+ "id": "3bac13f1-4ebc-4428-9230-af1dc6ab1ab9",
135
+ "name": "Geri Test",
136
+ "country": "AS",
137
+ "datastakeId": "PRJ-00000000107",
138
+ "title": "Geri Test",
139
+ "sectoralScope": "agricultureForestryAndOtherLandUse",
140
+ "sdgs": [],
141
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
142
+ "percentageCompletion": 33.33333333333333,
143
+ "projectDescription": "ABC Mangrove Senegal is an environmental and community-focused initiative dedicated to the restoration, preservation, and sustainable management of mangrove ecosystems in Senegal.",
144
+ "mainImage": [
145
+ {
146
+ "url": "https://cdn.straatos.io/dev/company-df5378ae-704a-4a46-93b2-336cc5341456/user-e8d71443-339f-4b45-bbf5-22f0e5f09600/screenshot-from-2024-11-11-09-29-22-1758200879648.png",
147
+ "path": "dev/company-df5378ae-704a-4a46-93b2-336cc5341456/user-e8d71443-339f-4b45-bbf5-22f0e5f09600/screenshot-from-2024-11-11-09-29-22-1758200879648.png",
148
+ "name": "screenshot-from-2024-11-11-09-29-22-1758200879648.png",
149
+ "size": 414991,
150
+ "type": "image/png"
151
+ }
152
+ ],
153
+ "author": {
154
+ "name": "Geri SHPK",
155
+ "country": "AL",
156
+ "category": "civilSociety",
157
+ "subCategory": "internationalNGO"
158
+ }
159
+ },
160
+ {
161
+ "_id": "688c66c62fb165d7a389fbf7",
162
+ "createdAt": "2025-08-01T07:03:34.501Z",
163
+ "updatedAt": "2025-08-01T07:03:34.501Z",
164
+ "form": "straatosProject",
165
+ "id": "39fd860e-412c-456f-9b70-a67d4dc82433",
166
+ "name": "test 777",
167
+ "country": "AL",
168
+ "datastakeId": "PRJ-00000000109",
169
+ "title": "test 777",
170
+ "sectoralScope": "agricultureForestryAndOtherLandUse",
171
+ "sdgs": [],
172
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
173
+ "author": {
174
+ "name": "Geri SHPK",
175
+ "country": "AL",
176
+ "category": "civilSociety",
177
+ "subCategory": "internationalNGO"
178
+ }
179
+ },
180
+ {
181
+ "_id": "68927b8d3c4650496ed9003f",
182
+ "createdAt": "2025-08-05T00:00:00.000Z",
183
+ "updatedAt": "2025-08-05T00:00:00.000Z",
184
+ "form": "straatosProject",
185
+ "id": "468204d2-28d6-4097-a362-ad12d642bf4f",
186
+ "name": "Current",
187
+ "country": "AL",
188
+ "datastakeId": "PRJ-00000000111",
189
+ "title": "Current",
190
+ "mainImage": [
191
+ {
192
+ "url": "https://cdn.straatos.io/dev/company-df5378ae-704a-4a46-93b2-336cc5341456/user-e8d71443-339f-4b45-bbf5-22f0e5f09600/screenshot-from-2024-11-28-12-41-52-1754465225229.png",
193
+ "path": "dev/company-df5378ae-704a-4a46-93b2-336cc5341456/user-e8d71443-339f-4b45-bbf5-22f0e5f09600/screenshot-from-2024-11-28-12-41-52-1754465225229.png",
194
+ "name": "screenshot-from-2024-11-28-12-41-52-1754465225229.png",
195
+ "size": 92090,
196
+ "type": "image/png"
197
+ }
198
+ ],
199
+ "sectoralScope": "energy",
200
+ "sdgs": [],
201
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
202
+ "percentageCompletion": 17.647058823529413,
203
+ "projectDescription": "testing testing testing",
204
+ "author": {
205
+ "name": "Geri SHPK",
206
+ "country": "AL",
207
+ "category": "civilSociety",
208
+ "subCategory": "internationalNGO"
209
+ }
210
+ },
211
+ {
212
+ "_id": "68985abea7d389ab99d611e4",
213
+ "createdAt": "2025-08-10T00:00:00.000Z",
214
+ "updatedAt": "2025-08-10T00:00:00.000Z",
215
+ "form": "project",
216
+ "id": "c949c1a4-f83d-45fa-820f-0ee56bc023ba",
217
+ "name": "project 1",
218
+ "country": "AL",
219
+ "datastakeId": "PRJ-00000000112",
220
+ "title": "project 1",
221
+ "mainImage": [
222
+ {
223
+ "url": "https://cdn.straatos.io/dev/company-df5378ae-704a-4a46-93b2-336cc5341456/user-e8d71443-339f-4b45-bbf5-22f0e5f09601/abcmangrove_allcot-10-1756470770827.jpg",
224
+ "path": "dev/company-df5378ae-704a-4a46-93b2-336cc5341456/user-e8d71443-339f-4b45-bbf5-22f0e5f09601/abcmangrove_allcot-10-1756470770827.jpg",
225
+ "name": "abcmangrove_allcot-10-1756470770827.jpg",
226
+ "size": 146120,
227
+ "type": "image/jpeg"
228
+ }
229
+ ],
230
+ "sectoralScope": "agricultureForestryAndOtherLandUse",
231
+ "sdgs": [],
232
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
233
+ "percentageCompletion": 25,
234
+ "author": {
235
+ "name": "Geri SHPK",
236
+ "country": "AL",
237
+ "category": "civilSociety",
238
+ "subCategory": "internationalNGO"
239
+ }
240
+ },
241
+ {
242
+ "_id": "68c3e1817ce6a6d7d8c16250",
243
+ "createdAt": "2025-09-12T00:00:00.000Z",
244
+ "updatedAt": "2025-09-12T00:00:00.000Z",
245
+ "form": "project",
246
+ "id": "b7a6ce62-dda3-4ffd-8f5a-e9721e15fe9f",
247
+ "name": "test fran",
248
+ "country": "CL",
249
+ "datastakeId": "PRJ-00000000113",
250
+ "title": "test fran",
251
+ "sectoralScope": "agricultureForestryAndOtherLandUse",
252
+ "sdgs": [],
253
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
254
+ "percentageCompletion": 25,
255
+ "author": {
256
+ "name": "Geri SHPK",
257
+ "country": "AL",
258
+ "category": "civilSociety",
259
+ "subCategory": "internationalNGO"
260
+ }
261
+ },
262
+ {
263
+ "_id": "68cab88001892530c66c3079",
264
+ "createdAt": "2025-09-17T13:32:48.646Z",
265
+ "updatedAt": "2025-09-17T13:32:48.646Z",
266
+ "form": "project",
267
+ "id": "a64070b0-74bc-4694-8e29-8006f9de70f7",
268
+ "name": "Pietra's Project",
269
+ "country": "AL",
270
+ "datastakeId": "PRJ-00000000114",
271
+ "title": "Pietra's Project",
272
+ "sectoralScope": "fugitiveEmissionsFromIndustrialGases",
273
+ "sdgs": [],
274
+ "authorId": "df5378ae-704a-4a46-93b2-336cc5341456",
275
+ "percentageCompletion": 25,
276
+ "author": {
277
+ "name": "Geri SHPK",
278
+ "country": "AL",
279
+ "category": "civilSociety",
280
+ "subCategory": "internationalNGO"
281
+ }
282
+ }
283
+ ];
284
+
285
+ export const storyConfig = {
286
+ data: transformProjectsToGlobeData(sampleProjects),
287
+ type: "project",
288
+ primaryLink: true,
289
+ showSider: true,
290
+ siderTitle: "Project Details",
291
+ renderTooltip: (project) => {
292
+ return [
293
+ { label: "Project ID", value: project.projectData?.datastakeId || "N/A" },
294
+ { label: "Sector", value: project.projectData?.sectoralScope || "N/A" },
295
+ { label: "Completion", value: `${Math.round(project.projectData?.percentageCompletion || 0)}%` },
296
+ { label: "Country", value: project.projectData?.country || "N/A" },
297
+ { label: "Author", value: project.projectData?.author || "N/A" },
298
+ { label: "Created", value: project.projectData?.createdAt ? new Date(project.projectData.createdAt).toLocaleDateString() : "N/A" }
299
+ ];
300
+ },
301
+ renderSider: (activeMarker) => {
302
+ const project = activeMarker.data;
303
+ const projectData = project.projectData;
304
+
305
+ return (
306
+ <div className="project-sider">
307
+ {projectData?.mainImage && (
308
+ <div className="project-image mb-3">
309
+ <img
310
+ src={projectData.mainImage}
311
+ alt={project.name}
312
+ style={{ width: '100%', height: '200px', objectFit: 'cover', borderRadius: '8px' }}
313
+ />
314
+ </div>
315
+ )}
316
+
317
+ <div className="project-details">
318
+ <div className="detail-row mb-2">
319
+ <span className="label">Project ID:</span>
320
+ <span className="value">{projectData?.datastakeId || "N/A"}</span>
321
+ </div>
322
+
323
+ <div className="detail-row mb-2">
324
+ <span className="label">Sectoral Scope:</span>
325
+ <span className="value">{projectData?.sectoralScope || "N/A"}</span>
326
+ </div>
327
+
328
+ <div className="detail-row mb-2">
329
+ <span className="label">Completion:</span>
330
+ <span className="value">{Math.round(projectData?.percentageCompletion || 0)}%</span>
331
+ </div>
332
+
333
+ <div className="detail-row mb-2">
334
+ <span className="label">Country:</span>
335
+ <span className="value">{projectData?.country || "N/A"}</span>
336
+ </div>
337
+
338
+ <div className="detail-row mb-2">
339
+ <span className="label">Author:</span>
340
+ <span className="value">{projectData?.author || "N/A"}</span>
341
+ </div>
342
+
343
+ <div className="detail-row mb-2">
344
+ <span className="label">Created:</span>
345
+ <span className="value">
346
+ {projectData?.createdAt ? new Date(projectData.createdAt).toLocaleDateString() : "N/A"}
347
+ </span>
348
+ </div>
349
+
350
+ {project.description && (
351
+ <div className="detail-row mt-3">
352
+ <span className="label">Description:</span>
353
+ <p className="value mt-1" style={{ fontSize: '14px', lineHeight: '1.4' }}>
354
+ {project.description}
355
+ </p>
356
+ </div>
357
+ )}
358
+ </div>
359
+ </div>
360
+ );
361
+ },
362
+ onClickLink: (project) => {
363
+ console.log('Project clicked:', project);
364
+ // You can add navigation logic here
365
+ }
366
+ };
@@ -17,11 +17,6 @@ VerticalSteps.args = {
17
17
  { title: "Step 1", description: "This is the first step" },
18
18
  { title: "Step 2", description: "This is the second step" },
19
19
  { title: "Step 3", description: "This is the third step" },
20
- { title: "Step 3", description: "This is the third step" },
21
- { title: "Step 3", description: "This is the third step" },
22
- { title: "Step 3", description: "This is the third step" },
23
- { title: "Step 3", description: "This is the third step" },
24
- { title: "Step 3", description: "This is the third step" },
25
20
  ],
26
21
  };
27
22
 
@@ -3,35 +3,16 @@ import { Steps } from "antd";
3
3
  import Widget from "../../Dashboard/Widget/index.jsx";
4
4
  import PropTypes from "prop-types";
5
5
 
6
- const DAFSteps = ({ direction = "vertical", current = 0, items = [], scrollThreshold = 4 }) => {
7
- const shouldScroll = items.length > scrollThreshold;
8
-
9
- const scrollableStyles = shouldScroll ? {
10
- ...(direction === "vertical" ? {
11
- maxHeight: "300px",
12
- overflowY: "auto",
13
- overflowX: "hidden"
14
- } : {
15
- maxWidth: "100%",
16
- overflowX: "auto",
17
- overflowY: "hidden",
18
- whiteSpace: "nowrap"
19
- }),
20
- padding: "8px",
21
- marginRight: direction === "vertical" ? "-8px" : "0",
22
- paddingRight: direction === "vertical" ? "16px" : "8px"
23
- } : {};
24
-
6
+ const DAFSteps = ({ direction = "vertical", current = 0, items = [] }) => {
25
7
  return (
26
- <Widget title="Life Cycle" className="with-border-header">
27
- <div style={scrollableStyles}>
28
- <Steps
29
- direction={direction}
30
- current={current}
31
- items={items}
32
- />
33
- </div>
8
+ <><Widget title="Life Cycle" className="with-border-header">
9
+ <Steps
10
+ direction={direction}
11
+ current={current}
12
+ items={items}
13
+ />
34
14
  </Widget>
15
+ </>
35
16
  );
36
17
  };
37
18
 
@@ -44,14 +25,12 @@ DAFSteps.propTypes = {
44
25
  description: PropTypes.string,
45
26
  })
46
27
  ),
47
- scrollThreshold: PropTypes.number,
48
28
  };
49
29
 
50
30
  DAFSteps.defaultProps = {
51
31
  direction: "vertical",
52
32
  current: 0,
53
33
  items: [],
54
- scrollThreshold: 4,
55
34
  };
56
35
 
57
36
  export default DAFSteps;
@@ -525,227 +525,6 @@ const PdfForm = ({
525
525
 
526
526
  const organizedForm = useMemo(() => organizeFormByHeaders(form), [form]);
527
527
 
528
- // Constants for height calculations (same as PdfView)
529
- const PAGE_HEIGHT = 1587;
530
- const FOOTER_HEIGHT = 70;
531
- const HEADER_HEIGHT = 100;
532
-
533
- // Helper function to estimate tree node height based on content and type
534
- const estimateTreeNodeHeight = (key, config, value, level = 0) => {
535
- const baseHeight = 28; // Base height per tree node
536
- const indentHeight = level * 2; // Additional height per level
537
- let totalHeight = baseHeight + indentHeight;
538
-
539
- // Adjust height based on node type
540
- if (config?.type === 'header') {
541
- totalHeight += 15; // Headers need more space
542
- } else if (config?.type === 'textarea') {
543
- totalHeight += 40; // Textareas are taller
544
- } else if (config?.type === 'dataLink' || config?.type === 'dataLinkGroup') {
545
- totalHeight += 20; // Data links often have more content
546
- }
547
-
548
- // Add height for value content if it exists
549
- if (value && typeof value === 'string' && value.length > 50) {
550
- totalHeight += Math.ceil(value.length / 50) * 15; // Multi-line content
551
- }
552
-
553
- // Add height for children recursively
554
- if (config?.inputs) {
555
- const childKeys = Object.keys(config.inputs)
556
- .filter(childKey => {
557
- const childConfig = config.inputs[childKey];
558
- // Check showIf condition
559
- if (childConfig?.showIf && !evaluateShowIfCondition(childConfig.showIf, data)) {
560
- return false;
561
- }
562
- return true;
563
- })
564
- .sort((a, b) => {
565
- const positionA = config.inputs[a]?.position || 0;
566
- const positionB = config.inputs[b]?.position || 0;
567
- return positionA - positionB;
568
- });
569
-
570
- childKeys.forEach(childKey => {
571
- const childConfig = config.inputs[childKey];
572
- const childValue = value?.[childKey] || data?.[childKey];
573
- totalHeight += estimateTreeNodeHeight(childKey, childConfig, childValue, level + 1);
574
- });
575
- }
576
-
577
- // Handle array/repeated content
578
- if (Array.isArray(value)) {
579
- value.forEach(itemValue => {
580
- totalHeight += estimateTreeNodeHeight(key, config, itemValue, level);
581
- });
582
- }
583
-
584
- return totalHeight;
585
- };
586
-
587
- // Helper function to split section based on height constraints (goes deep if needed)
588
- const createHeightConstrainedSections = (sectionKey, section) => {
589
- const MAX_SECTION_HEIGHT = PAGE_HEIGHT - HEADER_HEIGHT - FOOTER_HEIGHT - 150; // Conservative buffer
590
- const subSections = [];
591
-
592
- // Get all top-level items in the section with detailed analysis
593
- const topLevelItems = Object.keys(section)
594
- .filter(key => !(key === 'id' || key === 'label' || key === 'position' || key === 'subTitle'))
595
- .map(key => ({
596
- key,
597
- config: section[key],
598
- estimatedHeight: estimateTreeNodeHeight(key, section[key], data?.[key]),
599
- canSplit: section[key]?.inputs && Object.keys(section[key].inputs).length > 1 // Can this item be further split?
600
- }))
601
- .sort((a, b) => (a.config?.position || 0) - (b.config?.position || 0));
602
-
603
- let currentSubSection = {
604
- ...section,
605
- };
606
-
607
- // Remove all items from the base section structure
608
- Object.keys(section).forEach(key => {
609
- if (!(key === 'id' || key === 'label' || key === 'position' || key === 'subTitle')) {
610
- delete currentSubSection[key];
611
- }
612
- });
613
-
614
- let currentHeight = 80; // Base height for section header
615
- let subSectionIndex = 0;
616
-
617
- topLevelItems.forEach((item, index) => {
618
- const { key, config, estimatedHeight, canSplit } = item;
619
-
620
- // If a single item is too large and can be split, split it at the child level
621
- if (estimatedHeight > MAX_SECTION_HEIGHT * 0.8 && canSplit) {
622
- // Split this large item into smaller parts
623
- const childSplits = splitLargeItem(key, config, data?.[key], MAX_SECTION_HEIGHT * 0.6);
624
-
625
- childSplits.forEach((splitItem, splitIndex) => {
626
- // Check if current subsection has room
627
- if (currentHeight + splitItem.estimatedHeight > MAX_SECTION_HEIGHT && Object.keys(currentSubSection).length > 4) {
628
- // Save current sub-section
629
- subSections.push({
630
- key: `${sectionKey}_part_${subSectionIndex}`,
631
- section: { ...currentSubSection },
632
- title: section.label
633
- });
634
-
635
- // Start new sub-section
636
- currentSubSection = {
637
- id: section.id,
638
- label: section.label,
639
- position: section.position + subSectionIndex + 1,
640
- subTitle: section.subTitle
641
- };
642
- currentHeight = 80;
643
- subSectionIndex++;
644
- }
645
-
646
- // Add split item to current sub-section
647
- currentSubSection[splitItem.key] = splitItem.config;
648
- currentHeight += splitItem.estimatedHeight;
649
- });
650
- } else {
651
- // Regular processing for items that fit or can't be split
652
- // Check if adding this item would exceed height limit
653
- if (currentHeight + estimatedHeight > MAX_SECTION_HEIGHT && Object.keys(currentSubSection).length > 4) {
654
- // Save current sub-section
655
- subSections.push({
656
- key: `${sectionKey}_part_${subSectionIndex}`,
657
- section: { ...currentSubSection },
658
- title: section.label
659
- });
660
-
661
- // Start new sub-section
662
- currentSubSection = {
663
- id: section.id,
664
- label: section.label,
665
- position: section.position + subSectionIndex + 1,
666
- subTitle: section.subTitle
667
- };
668
- currentHeight = 80;
669
- subSectionIndex++;
670
- }
671
-
672
- // Add item to current sub-section
673
- currentSubSection[key] = config;
674
- currentHeight += estimatedHeight;
675
- }
676
- });
677
-
678
- // Add the final sub-section if it has content
679
- if (Object.keys(currentSubSection).length > 4) {
680
- subSections.push({
681
- key: subSectionIndex === 0 ? sectionKey : `${sectionKey}_part_${subSectionIndex}`,
682
- section: currentSubSection,
683
- title: section.label
684
- });
685
- }
686
-
687
- return subSections.length > 0 ? subSections : [{
688
- key: sectionKey,
689
- section: section,
690
- title: section.label
691
- }];
692
- };
693
-
694
- // Helper function to split large items at the child level
695
- const splitLargeItem = (parentKey, parentConfig, parentValue, maxHeight) => {
696
- if (!parentConfig?.inputs) {
697
- return [{ key: parentKey, config: parentConfig, estimatedHeight: estimateTreeNodeHeight(parentKey, parentConfig, parentValue) }];
698
- }
699
-
700
- const childItems = Object.keys(parentConfig.inputs)
701
- .filter(childKey => {
702
- const childConfig = parentConfig.inputs[childKey];
703
- return !childConfig?.showIf || evaluateShowIfCondition(childConfig.showIf, data);
704
- })
705
- .map(childKey => ({
706
- key: childKey,
707
- config: parentConfig.inputs[childKey],
708
- estimatedHeight: estimateTreeNodeHeight(childKey, parentConfig.inputs[childKey], parentValue?.[childKey])
709
- }))
710
- .sort((a, b) => (a.config?.position || 0) - (b.config?.position || 0));
711
-
712
- const splits = [];
713
- let currentSplit = {
714
- key: `${parentKey}_part_0`,
715
- config: {
716
- ...parentConfig,
717
- inputs: {}
718
- },
719
- estimatedHeight: 40 // Base height for parent structure
720
- };
721
- let splitIndex = 0;
722
-
723
- childItems.forEach(child => {
724
- if (currentSplit.estimatedHeight + child.estimatedHeight > maxHeight && Object.keys(currentSplit.config.inputs).length > 0) {
725
- splits.push(currentSplit);
726
- splitIndex++;
727
- currentSplit = {
728
- key: `${parentKey}_part_${splitIndex}`,
729
- config: {
730
- ...parentConfig,
731
- label: parentConfig.label,
732
- inputs: {}
733
- },
734
- estimatedHeight: 40
735
- };
736
- }
737
-
738
- currentSplit.config.inputs[child.key] = child.config;
739
- currentSplit.estimatedHeight += child.estimatedHeight;
740
- });
741
-
742
- if (Object.keys(currentSplit.config.inputs).length > 0) {
743
- splits.push(currentSplit);
744
- }
745
-
746
- return splits.length > 0 ? splits : [{ key: parentKey, config: parentConfig, estimatedHeight: estimateTreeNodeHeight(parentKey, parentConfig, parentValue) }];
747
- };
748
-
749
528
  const pdfConfig = useMemo(() => {
750
529
  const sections = [];
751
530
 
@@ -756,32 +535,27 @@ const PdfForm = ({
756
535
  return;
757
536
  }
758
537
 
759
- // Create height-constrained sub-sections
760
- const subSections = createHeightConstrainedSections(sectionKey, section);
761
-
762
- subSections.forEach(({ key, section: subSection, title }) => {
763
- sections.push({
764
- render: () => (
765
- <div key={key} className="pdf-form-section">
766
- <PdfFormContent
767
- form={{ [key]: { ...subSection, label: title } }}
768
- data={data}
769
- t={t}
770
- user={user}
771
- title={formName}
772
- source={source}
773
- version={version}
774
- getApiBaseUrl={getApiBaseUrl}
775
- getAppHeader={getAppHeader}
776
- app={app}
777
- />
778
- </div>
779
- ),
780
- style: {
781
- marginBottom: '20px',
782
- padding: '0 20px'
783
- }
784
- });
538
+ sections.push({
539
+ render: () => (
540
+ <div key={sectionKey} className="pdf-form-section">
541
+ <PdfFormContent
542
+ form={{ [sectionKey]: section }}
543
+ data={data}
544
+ t={t}
545
+ user={user}
546
+ title={formName}
547
+ source={source}
548
+ version={version}
549
+ getApiBaseUrl={getApiBaseUrl}
550
+ getAppHeader={getAppHeader}
551
+ app={app}
552
+ />
553
+ </div>
554
+ ),
555
+ style: {
556
+ marginBottom: '20px',
557
+ padding: '0 20px'
558
+ }
785
559
  });
786
560
  });
787
561
 
@@ -144,7 +144,7 @@ export const filterInputs = (form, data, repeatValues) => {
144
144
  }
145
145
 
146
146
  export const getNkey = (namespace) => {
147
- if (['location', 'scl', 'village', 'event', 'incidents', 'corrective-actions', 'testimonials', 'initiatives', 'victims', 'pictures', 'documents', 'lir', 'sp', 'im', 'sci', 'bpe', 'gm'].includes(namespace)) {
147
+ if (['location', 'scl', 'village', 'event', 'incidents', 'corrective-actions', 'testimonials', 'initiatives', 'victims', 'pictures', 'documents', 'lir', 'sp', 'im', 'sci', 'bpe', 'gm', 'project-readiness'].includes(namespace)) {
148
148
  return 'scoping';
149
149
  }
150
150