PageSourceSearch

https://emit-beta-v2.replit.app/routes/modules.js

js emit-beta-v2.replit.app collected 2026-10-03 03:50:11 UTC 16,969 bytes, 354 lines download raw bytes

1export default {
2    template: '#modules',
3    props: ['version','profile'], // version required for integrity checks, maybe kill?
4    data() {
5        return {
6            span: 21,
7            view: 'create',
8            mode: 'CREATE',
9            module: '',
10            proj: {
11                name: '',
12                start: '',
13                proj_type: '',
14                state: '',
15                field_office: '',
16                county: '',
17                description: ''
18            },
19            errors: [false,false,false,false,false,false],
20            projs: [],
21            qry_state: '',
22            qry_field: '',
23            filter: '',
24            visCols: [ 'desc', 'proj_type', 'field_office', 'created', 'status', 'actions' ],
25            columns: [
26                { name: 'desc', label: 'Name', field: row => row.name, align: 'left' },
27                { name: 'proj_type', label: 'Project Type', field: 'proj_type', align: 'left', sortable: true },
28                { name: 'state', label: 'State', field: 'state', align: 'left', sortable: true },
29                { name: 'county', label: 'County', field: 'county', align: 'left' },
30                { name: 'field_office', label: 'Field Office', field: 'field_office', align: 'left' },
31                { name: 'created', label: 'Create Date', field: 'created', align: 'left', sortable: true },
32                { name: 'last_access', label: 'Last Access Date', field: 'last_access', align: 'left', sortable: true },
33                { name: 'status', label: 'Status', field: 'status', align: 'center', sortable: true },
34                { name: 'actions', label: 'Action Options', align: 'right' }
35            ],
36            page: { rowsPerPage: 12 },
37            admin: false,
38            selected: {},
39            share_email: '',
40            access_level: null,
41            status: null,
42        }
43    },
44    created() {
45        //this.profile = emit.getProp('profile');
46        this.isLoaded = emit.getProp('loaded');
47        this.modules = emit.qry({ typ: 'find', coll: 'modules', q: { name: { '$ne' : null }} }); // Open all, but only enable or check for user mods
48        this.myModules = this.modules.filter(mod => {
49            // if (this.profile.modules.indexOf(mod.name) != -1) { return mod }
50            if (mod.name == 'fluids') { return mod }
51        });
52        this.module = this.myModules[0];
53        this.proj_types = [];
54        this.states = (this.profile.acct_type == 'internal' && this.profile.title != 'Air Resource Specialist') ? [this.profile.state] : 
55            ['Alaska','Arizona','California','Colorado','Eastern States','Idaho','Montana / Dakotas','Oregon / Washington','Nevada','New Mexico','Utah','Wyoming'];
56        this.fields = [];
57        this.counties = [];
58
59        this.dateOpts = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
60        this.statusOpts = [{label: 'Active', value: 'active'},{label: 'Reject', value: 'rejected'},{label: 'Accept', value: 'accepted'}];  
61        this.shared = false;
62    },
63    computed: {
64        isGuest() { return firebase.auth().currentUser.email == '[email protected]' },
65        internal() { return this.profile.acct_type == 'internal' },
66        isARS() { return this.profile.acct_type == 'internal' && this.profile.title == 'Air Resource Specialist'},
67        owner() { return firebase.auth().currentUser.uid == this.selected.owner },
68        canShare() { return (this.owner || this.selected.shared[firebase.auth().currentUser.uid] && this.selected.shared[firebase.auth().currentUser.uid].access_level == 'Full-Write') ? true : false },
69        defaultProj() { return this.selected.owner == 'default' },
70        versionMatch() { return this.version == this.selected.model_version }
71    },
72    mounted() {
73
74    },
75    methods: {
76        getProjs() { return emit.getProp('projects') },
77        createProj() {
78            if (this.isGuest) {
79                this.$q.notify({ message: `No Guest access to ${this.mode.toUpperCase()} function.`, color: 'red', icon: 'error', textColor: 'grey-1',
80                    actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
81                }); return;
82            }
83
84            if (this.module.name != 'fluids') { // PLACEHOLDER SCRIPT FOR FLUIDS ONLY SUPPORT
85                this.$q.notify({ color: 'red', icon: 'error', textColor: 'grey-1', message: 'TEMP ERROR: Only FLUIDS module available'});
86                return;
87            }
88
89            let flds = ['name','start','proj_type','state','field_office','county'];
90            for (let indx = 0; indx < flds.length; indx++) {
91                if (this.proj[flds[indx]] == '' || this.proj[flds[indx]] == null) {
92                    this.errors.splice(indx, 1, true); 
93                } else { this.errors.splice(indx, 1, false) }
94            }
95
96            if (this.errors.indexOf(true) == -1) {
97                let proj = Object.assign({}, this.proj);
98                proj['module'] = this.module.name;
99                proj['agency_review'] = false;
100                proj['created'] = new Date();
101                proj['last_access'] = new Date();
102                proj['last_user'] = '';
103                proj['access_cnt'] = 0;
104                proj['model_version'] = (this.mode == 'CREATE') ? this.version : '';
105                proj['owner'] = firebase.auth().currentUser.uid;
106                proj['status'] = 'Active';
107                proj['shared'] = [];
108
109                this.$eventHub.$emit('loading', true);
110                if (this.mode == 'CREATE') {
111                    emit.fb({ op: 'create_proj', proj: proj, callback: this.callback });
112                } else { // COPY
113                    emit.fb({ op: 'copy_proj', proj: proj, proj_id: this.selected._id, callback: this.callback });
114                }
115            }
116        },
117        cType(status) {
118            switch (status) {
119                case 'Active': return 'green'; break;
120                case 'Submitted': return 'purple'; break;
121                case 'Rejected': return 'red'; break;
122                case 'Accepted': return 'light-blue'; break;
123                case 'Final': return 'blue-grey'; break;
124            }
125        },
126        iType(status) {
127            switch (status) {
128                case 'Active': return 'cached'; break;
129                case 'Submitted': return 'info'; break;
130                case 'Rejected': return 'report_problem'; break;
131                case 'Accepted': return 'done'; break;
132                case 'Final': return 'lock'; break;
133            }
134        },
135        editProj(id) {
136            this.selected = this.projs.filter(proj => proj._id == id)[0];
137
138            if (this.isLoaded) {
139                this.$q.notify({ message: 'Cannot LOAD project, the module slot is full', color: 'red',
140                    actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
141                }); return;
142            }
143
144            let update = (this.canShare || this.internal) ? true : false;
145            this.$q.loading.show();
146            emit.fb({ op: 'get_proj', proj_id: id, update: update });
147        },
148        copyProj(id) {
149            this.selected = this.projs.filter(proj => proj._id == id)[0];
150
151            if (!this.canShare && !this.defaultProj) { 
152                this.$q.notify({ message: 'Cannot COPY project that is not default, owned, or shared in full.', 
152color: 'red',
153                    actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
154                }); return;
155            } 
156            if (this.isLoaded) {
157                this.$q.notify({ message: 'Cannot COPY project, the module slot is full', color: 'red',
158                    actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
159                }); return;
160            }
161
162            this.proj.proj_type = this.selected.proj_type;
163            this.proj.state = this.selected.state;
164            this.view = 'create';
165            this.mode = 'COPY';
166        },
167        deleteProj(id) {
168            this.selected = this.projs.filter(proj => proj._id == id)[0];
169            let msg = '';
170
171            if (this.isLoaded) { msg = 'Cannot DELETE project while module slot is full' }
172            if (!this.canShare) { msg = 'Cannot DELETE project that is not owned.' }
173            if (this.selected.status == 'Accepted' || this.selected.status == 'Final') { msg = 'Cannot DELETE ineligible project' }
174
175            if (msg) {
176                this.$q.notify({ message: msg, color: 'red',
177                    actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
178                }); return;
179            }   
180
181            this.$q.dialog({ title: 'Confirm Delete', message: 'This action cannot be undone.',
182                cancel: true, 
183                persistent: true
184            }).onOk(() => { 
185                this.admin = false;
186                emit.fb({ op: 'delete_proj', proj_id: id, callback: this.callback }); 
187            });
188        },
189        openAdmin(id) {
190            this.selected = this.projs.filter(proj => proj._id == id)[0];
191            this.shared = this.selected.shared.hasOwnProperty(firebase.auth().currentUser.uid);
192            this.admin = true;
193            setTimeout(() => { 
194                let users = document.querySelectorAll('[data-share-hash]');
195                for (let n = 0; n < users.length; n++) {
196                    while (users[n].firstChild) { users[n].firstChild.remove() }
197                    let h = hashicon(users[n].dataset.shareHash, 30);
198                    users[n].appendChild(h);
199                }
200            }, 500);
201        },
202        shareProj() {
203            if (this.share_email == '' || this.access_level == null) { return }
204            let msg = null;
205
206            if (this.internal) { msg = 'Internal project sharing is NOT enabled' }
207            if (!this.canShare) { msg = 'Missing elevated SHARE credentials' }
208            if (this.share_email == '[email protected]' || this.share_email == firebase.auth().currentUser.email) { msg = 'Invalid share address'}
209
210            if (msg) {
211                this.$q.notify({ message: msg, color: 'red',
212                    actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
213                }); return;
214            }
215            if (!msg) {
216                emit.fb({ op: 'share_proj', type: 'share', proj_id: this.selected._id, share_email: this.share_email, access_level: this.access_level, callback: this.callback });
217            }
218
219            this.share_email = '';
220            this.access_level = null;
221        },
222        revokeShare(id) {
223            if (!this.canShare) { 
224                this.$q.notify({ message: 'Missing credentials to revoke SHARE function', color: 'red',
225                    actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
226                }); return;
227            }
228            emit.fb({ op: 'share_proj', type: 'revoke', proj_id: this.selected._id, user: id, callback: this.callback });
229        },
230        updateProj(fld) { // CONSIDER INDEPENDANT CALLS, there is really NO code savings here!!!
231            switch (fld) {
232                case 'agency_review': {
233                    if (!this.internal) { // should not be necessary, extra precaution
234                        this.$q.notify({ message: 'Missing credentials to set Agency Review', color: 'red',
235                            actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
236                        }); return;
237                    }
238                    if (this.selected.agency_review == true) {
239                        this.$q.dialog({
240                            title: 'Confirm Review', message: 'Make this project visible to External Agency NEPA reviewers?',
241                            cancel: true,
242                            persistent: true
243                        }).onOk(() => { 
244                            emit.fb({ op: 'update_proj', proj_id: this.selected._id, fld: 'agency_review', val: true, callback: this.callback });
245                        }).onCancel(() => {
246                            this.selected.agency_review = false;
247                        });
248                    } else { emit.fb({ op: 'update_proj', proj_id: this.selected._id, fld: 'agency_review', val: false, callback: this.callback }) }
249                } break;
250
251                case 'status':
252                    if (!this.canShare && !this.internal) { 
253                        this.$q.notify({ message: 'Missing credentials to set Status', color: 'red',
254                            actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
255                        }); return;
256                    }
257                    if (this.selected.status == 'Final') {
258                        this.$q.notify({ message: 'Cannot set Status of ineligible project', color: 'red',
259                            actions: [ { label: 'Dismiss', color: 'white', handler: () => { /* ... */ } } ]
260                        }); return;
261                    }
262
263                    this.$q.dialog({
264                        title: (this.status.value == 'Submitted') ? 'Confirm Submit' : 'Confirm Final',
265                        message: (this.status.value == 'Submitted') ? 'This action will notify agency authority that project data is ready for review. Continue?' :
266                            'Setting Status to Final means the project has been completed in the real world to the specifications outlined in project dataset (i.e. As-Built). Final action means that this project will no longer be editable. Continue?',
267                        cancel: true,
268                        persistent: true
269                    }).onOk(() => { 
270                        emit.fb({ op: 'update_proj', proj_id: this.selected._id, fld: 'status', val: this.status.value, callback: this.callback }); 
271                        this.status = null;
272                    });
273
274            }
275        },
276        arsQuery() {
277            this.projs = this.getProjs().filter(proj => proj.module == this.module.name && proj.field_office == this.qry_field);
278            if (this.projs.length == 0) {
279                emit.fb({ op: 'ars_query', fld: this.qry_field, callback: this.callback });
280            }
281        },
282        callback(typ, id) {
283            this.projs = this.getProjs().filter(proj => proj.module == this.module.name);
284
285            switch (typ) {
286                case 'create': {
287                    for (let n in this.proj) { this.proj[n] = '' } 
288                    this.mode = 'CREATE';
289                    this.editProj(id);
290                } break;
291                case 'copy': {
292                    for (let n in this.proj) { this.proj[n] = '' } 
293                    this.mode = 'CREATE';
294                    this.editProj(id);
295                } break;
296                case 'admin': { 
297                    let id = this.selected._id;
298                    this.openAdmin(id); 
299                } break;
300            }
301        }
302
303    },
304    watch: {
305        view() { 
306            this.admin = false;
307        },
308        module() { // should function for both create and query
309            this.admin = false;
310            this.qry_field = '';
311            this.projs = this.getProjs().filter(proj => proj.module == this.module.name);
312
313            for (let n in this.proj) { this.proj[n] = '' }
314            this.proj_types = this.module.proj_types.split(',');
315            this.errors.forEach((el, i) => { this.errors[i] = false });
316        },
317        admin() { this.status = null },
318
319        'proj.state': function() {
320            if (this.proj.state) {
321                if (this.profile.acct_type == 'internal' && this.profile.title != 'Air Resource Specialist') {
322                    this.fields = this.profile.flds;
323                } else {
324                    this.fields = emit.qry({ typ: 'lookup', q: { '$and': [ 
325                        { component: 'create' },
326                        { target: 'fields' },
327                        { filter: this.proj.state }
328                    ]} });
329                    this.proj.field_office = null;
330                    this.proj.county = null;
331                }
332            }
333        },
334        'proj.field_office': function() {
335            if (this.proj.field_office) {
336                this.counties = emit.qry({ typ: 'lookup', q: { '$and': [ 
337                    { module: 'all' },
338                    { component: 'create' },
339                    { target: 'counties' },
340                    { filter: this.proj.field_office }
341                ]} });
342                this.proj.county = null;
343            }
344        },
345        qry_state() {
346            this.fields = emit.qry({ typ: 'lookup', q: { '$and': [ 
347                { component: 'create' },
348                { target: 'fields' },
349                { filter: this.qry_state }
350            ]} });
351            this.qry_field = null;
352        },
353    }
354}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.