commit
2f28a7dfa7
8 changed files with 190 additions and 0 deletions
-
3Approval_Pending/config.json
-
1Approval_Pending/icon.svg
-
29Approval_Pending/manifest.json
-
7Approval_Pending/src/config.ts
-
104Approval_Pending/src/index.tsx
-
8Approval_Pending/src/runtime/widget.tsx
-
24Approval_Pending/src/styles.css
-
14Approval_Pending/tests/simple-widget.test.tsx
@ -0,0 +1,3 @@ |
|||||
|
{ |
||||
|
"exampleConfigProperty": "test" |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
<svg class="svg-icon" style="width: 1em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M526.791111 1005.037037h-329.955555c-61.62963 0-111.691852-50.062222-111.691852-111.691852V246.518519c0-61.62963 50.062222-111.691852 111.691852-111.691852h145.066666c21.617778-57.457778 74.145185-132.740741 168.770371-132.740741 101.451852 0 147.152593 85.712593 164.598518 132.740741h132.361482c61.62963 0 111.691852 50.062222 111.691851 111.691852v376.794074c0 20.859259-17.066667 37.925926-37.925925 37.925926s-37.925926-17.066667-37.925926-37.925926V246.518519c0-19.721481-16.118519-35.84-35.84-35.84h-160.237037c-17.825185 0-33.185185-12.515556-36.977778-29.771852-0.948148-3.982222-24.082963-102.968889-99.745185-102.968889-80.213333 0-103.537778 101.831111-103.727408 102.968889a37.736296 37.736296 0 0 1-36.977778 29.771852h-173.131851c-19.721481 0-35.84 16.118519-35.84 35.84v646.637037c0 19.721481 16.118519 35.84 35.84 35.84h329.955555c20.859259 0 37.925926 17.066667 37.925926 37.925925s-17.066667 38.115556-37.925926 38.115556z" fill="#FF8206" /><path d="M545.754074 211.057778h-76.420741c-10.24 0-18.773333-8.343704-18.773333-18.773334v-0.568888c0-10.24 8.343704-18.773333 18.773333-18.773334h76.420741c10.24 0 18.773333 8.343704 18.773333 18.773334v0.568888c0 10.24-8.533333 18.773333-18.773333 18.773334zM754.346667 387.602963H260.740741c-10.24 0-18.773333-8.343704-18.773334-18.773333v-0.568889c0-10.24 8.343704-18.773333 18.773334-18.773334h493.605926c10.24 0 18.773333 8.343704 18.773333 18.773334v0.568889c0 10.42963-8.533333 18.773333-18.773333 18.773333zM754.346667 564.337778H260.740741c-10.24 0-18.773333-8.343704-18.773334-18.773334v-0.568888c0-10.24 8.343704-18.773333 18.773334-18.773334h493.605926c10.24 0 18.773333 8.343704 18.773333 18.773334v0.568888c0 10.42963-8.533333 18.773333-18.773333 18.773334zM521.481481 741.072593H255.620741c-10.24 0-18.773333-8.343704-18.773334-18.773334v-0.568889c0-10.24 8.343704-18.773333 18.773334-18.773333H521.481481c10.24 0 18.773333 8.343704 18.773334 18.773333v0.568889c0 10.24-8.343704 18.773333-18.773334 18.773334zM769.137778 987.780741c-3.982222 0-8.154074-1.327407-11.567408-3.982222l-104.485926-80.213334a18.811259 18.811259 0 0 1-3.413333-26.548148c6.447407-8.343704 18.204444-9.860741 26.548148-3.413333l89.884445 69.214815 156.065185-189.44a18.962963 18.962963 0 0 1 26.737778-2.654815 18.962963 18.962963 0 0 1 2.654814 26.737777l-167.632592 203.662223c-3.982222 4.171852-9.291852 6.637037-14.791111 6.637037z" fill="#FFAF28" /></svg> |
||||
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
"name": "Approval_Pending", |
||||
|
"label": "Approval_Pending", |
||||
|
"type": "widget", |
||||
|
"version": "1.16.0", |
||||
|
"exbVersion": "1.16.0", |
||||
|
"author": "Tanii GIS Developer", |
||||
|
"description": "This is a barebones, simple widget. It includes an example of how to use the config file, and it includes a placeholder SVG icon. It intentionally does not include the following so you can use this to start from scratch: i18n translations, a Settings panel, unit tests. See the documentation for more information on how to implement these features.", |
||||
|
"copyright": "", |
||||
|
"license": "http://www.apache.org/licenses/LICENSE-2.0", |
||||
|
"properties": { |
||||
|
"hasSettingPage": false |
||||
|
}, |
||||
|
"widgets": [ |
||||
|
{ |
||||
|
"uri": "Approval_Pending", |
||||
|
"label": "Approval_Pending", |
||||
|
"thumbnail": "thumbnail.png", |
||||
|
"description": "Summarizes Street Light, Highmast, and Pillar Box counts by zone." |
||||
|
} |
||||
|
], |
||||
|
"framework": "react", |
||||
|
"translatedLocales": ["en"], |
||||
|
"dataSources": [], |
||||
|
"elements": [] |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,7 @@ |
|||||
|
import { type ImmutableObject } from 'seamless-immutable' |
||||
|
|
||||
|
export interface Config { |
||||
|
exampleConfigProperty: string |
||||
|
} |
||||
|
|
||||
|
export type IMConfig = ImmutableObject<Config> |
||||
@ -0,0 +1,104 @@ |
|||||
|
import React, { useEffect, useState } from 'react'; |
||||
|
import FeatureLayer from '@arcgis/core/layers/FeatureLayer'; |
||||
|
import { Box, Typography } from '@mui/material'; |
||||
|
|
||||
|
interface ZoneData { |
||||
|
zone_no: string; |
||||
|
pendingCount: number; |
||||
|
toBeVerifiedCount: number; |
||||
|
} |
||||
|
|
||||
|
const ZoneIntimationOnly = () => { |
||||
|
const [zoneData, setZoneData] = useState<ZoneData[]>([]); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
fetchData(); |
||||
|
}, []); |
||||
|
|
||||
|
const normalizeZone = (zone: any) => { |
||||
|
if (zone == null) return ''; |
||||
|
const s = String(zone); |
||||
|
const match = s.match(/\d+/); |
||||
|
return match ? match[0].padStart(2, '0') : ''; |
||||
|
}; |
||||
|
|
||||
|
const fetchData = async () => { |
||||
|
const BUILDING_LAYER_URL = |
||||
|
'https://gisgcc.chennaicorporation.gov.in/server/rest/services/Revenue/Building_Updation/FeatureServer/0'; |
||||
|
|
||||
|
const buildingLayer = new FeatureLayer({ |
||||
|
url: BUILDING_LAYER_URL |
||||
|
}); |
||||
|
|
||||
|
const query = buildingLayer.createQuery(); |
||||
|
query.outFields = ['zone', 'approval']; |
||||
|
query.where = '1=1'; |
||||
|
query.returnGeometry = false; |
||||
|
query.num = 2000; |
||||
|
|
||||
|
let features: any[] = []; |
||||
|
let offset = 0; |
||||
|
|
||||
|
while (true) { |
||||
|
query.start = offset; |
||||
|
const res = await buildingLayer.queryFeatures(query); |
||||
|
features = features.concat(res.features); |
||||
|
if (res.features.length < query.num) break; |
||||
|
offset += query.num; |
||||
|
} |
||||
|
|
||||
|
const zonePendingMap: Record<string, number> = {}; |
||||
|
const zoneTBVMap: Record<string, number> = {}; |
||||
|
|
||||
|
features.forEach(f => { |
||||
|
const rawZone = f.attributes?.zone ?? ''; |
||||
|
const approval = (f.attributes?.approval ?? '').trim().toLowerCase(); |
||||
|
|
||||
|
const zone = normalizeZone(rawZone); |
||||
|
if (!zone) return; |
||||
|
|
||||
|
if (approval === 'pending') { |
||||
|
zonePendingMap[zone] = (zonePendingMap[zone] || 0) + 1; |
||||
|
} |
||||
|
|
||||
|
if (approval === 'to be verified') { |
||||
|
zoneTBVMap[zone] = (zoneTBVMap[zone] || 0) + 1; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const allZones = new Set([ |
||||
|
...Object.keys(zonePendingMap), |
||||
|
...Object.keys(zoneTBVMap) |
||||
|
]); |
||||
|
|
||||
|
const result: ZoneData[] = Array.from(allZones).map(zone => ({ |
||||
|
zone_no: zone, |
||||
|
pendingCount: zonePendingMap[zone] || 0, |
||||
|
toBeVerifiedCount: zoneTBVMap[zone] || 0 |
||||
|
})); |
||||
|
|
||||
|
result.sort((a, b) => parseInt(a.zone_no, 10) - parseInt(b.zone_no, 10)); |
||||
|
|
||||
|
setZoneData(result); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<Box p={2} sx={{ maxHeight: 300, overflowY: 'auto' }}> |
||||
|
{zoneData.length === 0 ? ( |
||||
|
<Typography variant="body2" sx={{ color: 'white' }}> |
||||
|
No Pending or To Be Verified records found. |
||||
|
</Typography> |
||||
|
) : ( |
||||
|
zoneData.map(z => ( |
||||
|
<Typography key={z.zone_no} variant="body2" sx={{ color: 'white', mb: 1 }}> |
||||
|
<strong>Zone {z.zone_no}</strong> — Pending: |
||||
|
<strong> {z.pendingCount} </strong> | To be Verified: |
||||
|
<strong> {z.toBeVerifiedCount} </strong> |
||||
|
</Typography> |
||||
|
)) |
||||
|
)} |
||||
|
</Box> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default ZoneIntimationOnly; |
||||
@ -0,0 +1,8 @@ |
|||||
|
import { React, AllWidgetProps } from 'jimu-core'; |
||||
|
import ZoneAbstractWidget from '../index'; |
||||
|
|
||||
|
const Widget = (props: AllWidgetProps<any>) => { |
||||
|
return <ZoneAbstractWidget {...props} />; |
||||
|
}; |
||||
|
|
||||
|
export default Widget; |
||||
@ -0,0 +1,24 @@ |
|||||
|
.widget-container { |
||||
|
padding: 15px; |
||||
|
background: white; |
||||
|
border-radius: 8px; |
||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); |
||||
|
} |
||||
|
|
||||
|
h3 { |
||||
|
font-size: 18px; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.export-button { |
||||
|
margin-top: 10px; |
||||
|
background-color: #0078d4; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.table-container { |
||||
|
max-height: 400px; |
||||
|
overflow-y: auto; |
||||
|
border: 1px solid #ddd; |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,14 @@ |
|||||
|
import { React } from 'jimu-core' |
||||
|
import _Widget from '../src/runtime/widget' |
||||
|
import { widgetRender, wrapWidget } from 'jimu-for-test' |
||||
|
|
||||
|
const render = widgetRender() |
||||
|
describe('test simple widget', () => { |
||||
|
it('simple test', () => { |
||||
|
const Widget = wrapWidget(_Widget, { |
||||
|
config: { exampleConfigProperty: 'a' } |
||||
|
}) |
||||
|
const { queryByText } = render(<Widget widgetId="Widget_1" />) |
||||
|
expect(queryByText('exampleConfigProperty: a').tagName).toBe('P') |
||||
|
}) |
||||
|
}) |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue