npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mobile-asset-manager

v1.0.0

Published

An Ionic project

Readme

Mobile Asset Manager: Ionic 4 Capacitor app

Doc: https://capacitor.ionicframework.com/docs/

Project setup:

  1. look into pre-requisites section in this document.
  2. npm i --force

To Run App

  1. ionic build

// For following commands, replace your platform-value (ex: ios) in place of android 2. npx cap add android
3. For icon and splash screen refer https://capacitorjs.com/docs/guides/splash-screens-and-icons 4. npx cap copy android 5. npx cap sync android 6. npx cap open android

  • For any build issues or errors, please refer to this document for handling them.
    • In case if you faced any new-issue, please do update the document.
  • Note: once APK build is successful you can use ionic cap run android -l --external for live-reload

To Change App Version

  1. iOS : Info.plist(ios/App/App) -> CFBundleShortVersionString
  2. Android : build.gradle(android/app) -> versionName

Naming Conventions:

  • private variables are prefixed with _.

    // Example:
    private _privateVariable: string;
  • observables are suffixed with $.

    // Example:
    woDefinitions$: Observable<WorkDefintionInterface>;
  • if styles need to be applied, use class-names, don't attempt to style html-elements, or override styles using predefined-classes

  • while creating a new component, wrap the element with its 'selector' as a class and apply the styles, so that they are scoped to that page only

  <!-- html -->
  <div class="dashboard">
    <ion-card class="title">
        // content
    </ion-card>
  </div>

  <!-- css -->
  .dashboard {
      .title {
          // styles
      }
  }
  • ALWAYS unsubscribe the Subscriptions in ngOnDestroy

TODOs:

  • [ ] On SignOut: clear local-storage, ionic-storage and tables(if any)
  • [ ] if login-api fails, dismiss the loader try signing in while offline with BUSINESS_CONFIG.useFrontEgg = false
  • [ ] should make all interface fields for GET APIs as must, without it being optional interface, otherwise if fields are missing it will not insert in Table
  • [ ] make a common component for attachments, that has inputs as attachments, and contains toggle-able options to preview, delete, add attachment


index-9b0d46f4.js:11 [Ionic Warning]: The value provided to ion-datetime is out of bounds.

Min: {"month":6,"day":4,"year":2024,"hour":16,"minute":0}
Max: {"month":1,"day":1,"year":2030,"hour":23,"minute":59}
Value: {"year":2024,"month":5,"day":8,"hour":12,"minute":41,"ampm":"pm"}

TODO: styles:

  • all components to be tested & displayed for responsiveness, states(hover, focus, active, disabled), and variants(small, medium, large)
  • in Figma, we don't have colors for different status of buttons(like: hover, focus, active, disabled)
  • need any ripple-effect for buttons? if so, refer 'ion-ripple-effect' in ionic docs
  • 'not-available' status for buttons, should be disabled?, and should have a ripple-effect? what is the purpose of it?
  • use enums for all component classes, inputs
  • use a shared/default margin/padding for all components (like: margin: 2px 4px;) [check default-margin-for-components]
  • add validations for 'eam-counter'? with-in the component or in the parent-component?
    • there should be a min-width of 3-4 digit space in 'eam-counter' component, so that it doesn't shrink/expand when the value changes
    • it should be scaled as well, (transform: scale(1.5), relatively to the parent-component based on em or rem)
  • eam-tag to be used for priority as well(using icons)
  • eam-input-field :
    • create a common style-sheet and import for all type of inputs
    • apply state based styles (like: focus, hover, active, disabled) like for borders, background-colors, etc
    • the variants: small, medium, large, should be relative, like 1x, 1.5x, 2x, etc (check how to do in css/globally)
    • read-only, disabled, active states should be applied
    • handle different error-messages, like if age: 'should be less than 18', 'required', etc
  • consider accessibility?
  • if considering apple-devices, then apple specific icons? and how to test?

Maintaing Style Sheets:

  1. applying styles:
<!-- Always have a container tag prefixed with `eam-` -->
<div class="eam-container">
    <div class="eam-title">
        <!-- content -->
    </div>

    <div class="eam-input">
        <!-- input content -->
    </div>
</div>
/* imports (if any) */
@import 'flexbox.scss';

/* Define any local variables (if any) */
:host {
  --eam-title-color: #000;
  --eam-title-font-size: 1.5rem;

  --input-font-family: var(--font-family-primary);
  --input-font-size: var(--input-field-label-font-size);
  --input-font-weight: var(--font-weight-regular);
  --input-font-color: #000;
}

/* Then apply styles */
.eam-container {
  /* Order */
  /* 1. local/host variable styles to be overridden/declared */
  --eam-title-color: #fff;

  /* 2. use includes, extends and other scss methods (if any) */
  @include flexbox();

  /* 3. apply styles */
  .eam-title {
      color: var(--eam-title-color);
  
      &:hover {
          --eam-title-color: #f00;
      }
  }

  .eam-input {
      font-family: var(--input-font-family);
      font-size: var(--input-font-size);
      font-weight: var(--input-font-weight);

      /* pseudo-classes */
      &:hover {
          --input-font-color: #f00;
      }

      /* variants */
      &.small {
        --input-font-size: 1rem;
      }

      &.large {
        --input-font-size: 2rem;
      }

      /* states */
      &.disabled {
          --input-font-color: #ccc;
          cursor: not-allowed;
      }

      &.active {
          --input-font-color: #000;
      }
  }
}

Info:

  • [ ] firstValueFrom:-

           This function resolves the Promise with the first value emitted by the Observable.

Use Case: Use this when you are only interested in the first emission and want to ignore subsequent values.

  • [ ]lastValueFrom:-

          This function resolves the Promise with the last value emitted by the Observable before it completes.

Use Case: Use this when you want to wait for the Observable to complete and are interested in the final value it emits.

ToastService

-[ ] The ToastService is used to display toast messages in our Ionic application. It provides a method to show a toast with customizable messages, colors, and positions.

  • [ ] showToast(message: string, color: string = TOAST_COLOR.DANGER, position: ToastPositionOptions = TOAST_POSITION.BOTTOM): Promise

  • [ ] message (string): The text to be displayed in the toast. This text will be translated using the TranslateConfigService. we should pass the value as String only

  • [ ] color (string, optional): The color of the toast. Default is TOAST_COLOR.DANGER. Colors are defined in TOAST_COLOR constants(enums).

  • [ ] position (ToastPositionOptions, optional): The position of the toast on the screen. Can be 'bottom', 'top', or 'middle'. Default is TOAST_POSITION.BOTTOM.

Usage Example:

 this.toastService.showToast('Your message here', 'success', 'top');

 In this example, the toast will display a success message at the top of the screen.

TODOs for others in dev:

  • src/app/constants/local-storage.constant.ts
    • use TableNames4SQLiteDB for values
  • src\app\services\utils\application-cache-handling.service.ts
    • add || [] to avoid errors while accidentally spreading null or undefined
    • change the order of api-insertion or identify why values are not loading while doing ionic-serve
  • add: src/app/services/utils/field-mapping.service.ts
  • add: src/constants/field-mapping.constants.ts
    • automatic mapping of fields from one object to another
    • handleNewData (handles flag-D, inert-or-replace-into for observables, still relevant?)

https://testnode.propelapps.com/CLD/24B/getAllConditionEvents/" https://testnode.propelapps.com/CLD/24B/getAllWOFailures/300000158802121/300000160936508/" https://testnode.propelapps.com/CLD/24B/getAllFailureAssociations/" https://testnode.propelapps.com/CLD/24B/getAllFailureChains/" https://testnode.propelapps.com/CLD/24B/getAllWOFailuresBatches/1

Changes to be done as part of new DB support:

  • check the guides at setup-sqlite.md for official documentation of @capacitor-community/sqlite and its limitations and other info.
  • Take a look into ./electron/src/preload.ts adn check the contextBridge.exposeInMainWorld('CapacitorSQLite' ...) code which is used to expose the CapacitorSQLite object to the window object in the Electron app. and need to add any missing methods or properties to the CapacitorSQLite object, incase if you'er facing any errors. Few things I've observed:
  1. can't insert huge value for INTEGER:
  • Search for getWorkOrderId in work-order.component.ts
  1. date object can't be inserted, need to be converted as string
  • Search for addToTransactionHistory in work-order.component.ts and see that the date is converted to string
  • Check if there is a datatype for date, if not, then convert it to string and insert (check docs)
  1. If the datatype is a number, the default value should be null or 0, would recommend the usage of null over 0 to avoid any edge-case.
  • This would be required when you're saving a new record, and the value is not provided, so it should be null instead of 0. You can check the work-order.component.ts for reference, where in setWorkOrder() method, We're passing organizationId as null.
  1. you cannot insert undefined or null values, so you need to check for null values and replace them with '' or 0 or null based on the datatype.
  2. check work-order.page.ts on how to implement search-functionality, infinite-scroll, refresh on data-reload/delta-sync, handle route-params Things I couldn't fix or to be looked into:
  3. supposed to use .run, but using .query (there is other thing as .execute) when doing DML operations(like insert, update, delete). But due to consistent errors, for now, I've used .query for all operations.
  4. insertion should be done in one single transaction, but due to errors, I've used multiple transactions for now. (inserting one record at a time)
  5. test reset-db while using CapSQL DB
  6. delete db and db-files: DROP TABLE, delete/reset the db file, and then create a new db file
  7. Need to confirm, but: in activity-screen, user should be able to navigate forward, upon the complete insertion to DB or API success?
  • Right now, there seems to be a delay in the insertion to DB or the process, so the user is not able to navigate forward.
  1. In the provided insertToTableCapacitor method, if an error occurs during the insertion of a record within the for loop, the method immediately catches the error, logs it, and returns false. This means that as soon as any single insertion fails, the method will stop executing further insertions and exit. The insertions that occurred before the error will not be rolled back automatically; they will remain in the database. [I believe I've fixed, but confirm again]
  2. I've commented following code, as when org switches: it won't go throught this for MASTER and CONFIG APIs.
      // Determine if delta sync or initial sync needs to be handled
      if ((isDeltaSync && !JSON.parse(this.globalvariablesProvider.getAllAPISyncedStatus())) ||
         (!isDeltaSync && JSON.parse(this.globalvariablesProvider.getAllAPISyncedStatus()) && isMasterOrConfigApi)) {
        return { status: true, responsibility: responsibility, msg: 'No Content' };
    }

TODOs:

  • complete pending TODOs(search for: TODO)
  • retry for master or config apis
  • pull-down refresh in dashboard? (there is already a sync button, necessary?)
  • I've removed allFailureCodeWithBatches responsibility as it is not required, when we have FAILURE_CODE responsibility (why??)
  • issue: calling/inserting AllWoFailures twice
  • workOrderComponent.updateWorkOrder, workOrderComponent.createWorkOrder should be an event emitters or triggered upon a subject/other sort? why are we using viewChildren ?
  • make header a component
  • In WO List: search-bar : should it be on top always?(like overlay if user scrolls)
    • make search-bar a component(helps with debounce, distinctUntilChanged), header(for common changes) [refer figma]
    • search input minLength validation? (to reduce triggers)
  • csv api data insertion: should happen without the need of converting csv-to-json and then do insertion
  • eslint
  • in case of success, transaction result: delete that local record in respective table(not sure how it is working now)
  • in work-orders filter option should be a modal, so we can dismiss and pass the props for filtering wo-list
  • log errors and success(toggleable) details transaction in a file [same as inventory]
  • Log certain data based on a flag
  • on logout + login => org-page(with default org selected) but no activity screen?
  • when application gets killed in activity-screen, and user re-open the application: should direct to activity-screen with only pending APs called(but what about batch?)

Coding Conventions:

Naming Convention:
  • Subscription: Use the suffix Subscription. [ searchSubscription]
  • Observable: Use the suffix $. [ workOrdersUpdate$ ]
  • Subject: Use the suffix Subject$. [ searchSubject$ ]
  • BehaviorSubject: Use the suffix Subject$ (same as Subject).
  • if private: prefix with _. [ _searchSubscription ]
  • don't use forkJoin, but Promise.all or Promise.allSettled with and for proper error-handling
  • always use interfaces
  • translation
  • jsDoc
  • guard-clauses

| Field | isChainFlag | isNotChainFlag | | --- | --- | --- | | Failure Code | getAllFailureChains | | | Cause Code | getAllFailureChains | 0 | | Resoultion Code | 1 | 0 | SPRINT 2:

Currently Finished Tiles: WO, ASSET(view-only), METER READING

@Subramanian provided the SQL tables, need to be created in GCP (priority?? and estimations??) needs to finalise on the approval process by functional-team

WORK REQUEST WORK PERMIT

ASSET: [estimations: ??] Need to add POST APIs(create and update API), and show as categorize) a to the existing Asset Tile UI, Flow, TDD


GET_ALL: `SELECT * FROM ${TableNames4SQLiteDB.WORK_ORDER_HEADERS} ${BUSINESS_CONFIG.hideUnassignedWorkOrders ? `WHERE ${conditions.getAssignedToCondition()}` : ''}`,
    GET_WITH_LIMIT: `SELECT * FROM ${TableNames4SQLiteDB.WORK_ORDER_HEADERS} ${BUSINESS_CONFIG.hideUnassignedWorkOrders ? `WHERE ${conditions.getAssignedToCondition()}` : ''} LIMIT ${BUSINESS_CONFIG.queryLimitSize} OFFSET ?`,
    // implement fuzzy search: means what ever we're displaying in work-order-card, should be searchable
    GET_WITH_FILTER_AND_LIMIT: `SELECT * FROM ${TableNames4SQLiteDB.WORK_ORDER_HEADERS} ${BUSINESS_CONFIG.hideUnassignedWorkOrders ? `WHERE ${conditions.getAssignedToCondition()} AND` : 'WHERE'} (WorkOrderNumber LIKE ? OR assetDescription LIKE ? OR assetNumber LIKE ? ORDER BY StartDate Desc LIMIT ${BUSINESS_CONFIG.queryLimitSize} OFFSET ?`,
    GET_COUNT: `SELECT COUNT(*) AS count FROM ${TableNames4SQLiteDB.WORK_ORDER_HEADERS} ${BUSINESS_CONFIG.hideUnassignedWorkOrders ? `WHERE ${conditions.getAssignedToCondition()}` : ''}`,
    GET_BY_WORK_ORDER_NUMBER: `SELECT * FROM ${TableNames4SQLiteDB.WORK_ORDER_HEADERS} WHERE WorkOrderNumber = ? ${BUSINESS_CONFIG.hideUnassignedWorkOrders ? `AND ${conditions.getAssignedToCondition()}` : ''}`,
    GET_BY_WORK_ORDER_ID: `SELECT * FROM ${TableNames4SQLiteDB.WORK_ORDER_HEADERS} WHERE workOrderId = ? ${BUSINESS_CONFIG.hideUnassignedWorkOrders ? `AND ${conditions.getAssignedToCondition()}` : ''}`,

MAM-128:

  • add 'Attachment' tab
  • From /assetDetails API when "IsAttachmentAvailable" is "Y", then call geAttachements api
  • Ex: https://testnode.propelapps.com/CLD/23D/getAttachments/ASSET/INJ-MOLD-001/''/''/''/''

Steps:

  1. create attachments-card component:
  • command: ionic generate component components/attachments-card

  • should contain

    • can accept assetNumber, woNumber as input

    • display all attachments (file-name, uploaded-time)

    • should be able to upload new attachments(doc, docx, pdf, png)

    • preview of the file should be shown

    • on click of any attachment, open in new tab

    • should have a download-icon

    • on click of download-icon, download the file

sample ui: header: | Attachments | add-button |

each-card: | [ Thumbnail ] file-name | preview-icon | download-icon | delete-icon |

add-button: prompts to choose from gallery, camera or file-manager

Date - 6thNov -2024

When the backend team introduces a new field or column, please ensure it is added in three specific locations within the codebase to maintain consistency and functionality:

Why This Approach? Originally, table creation was based entirely on metadata. However, we’ve updated the process to align table creation with both the model interface and metadata fields. This enhancement ensures tables are structured based on the fields present in the interface, maintaining the metadata order for a consistent schema.

Model Interface:

Add the new field in the model interface. This can be done in any order, as the interface primarily defines available fields.

API Mapping Object (Work Order - Transactional API):

Update the mapping object for the specific API. Ensure the field appears in the same order as specified in the metadata, as this preserves a standardized structure across API requests and responses. Eg : private async getWorkOrderHeaders(isDeltaSync: boolean): Promise<API_RESPONSE_TYPE> { const tableName = TableNames4SQLiteDB.WORK_ORDER_HEADERS; const metadataUrl = getApiUrl('workOrderHeadersMetadata'); const lastSyncDate = await this.apiUtilService.getLastSyncDate(isDeltaSync, tableName); const listUrl = getApiUrl('workOrderHeaders') + /"${lastSyncDate}"; const workOrderInterfaceObject = workOrderMappingObject; // we need to map the object & should add respective field return this.fetchApisDataService.getDataFromApi(listUrl, metadataUrl, tableName, isDeltaSync, RESPONSIBILITY.WORK_ORDER_HEADERS, false, false, false, null, workOrderInterfaceObject); }

Mapping Business Configuration for Table Creation and Data Insertion

In this approach, we use a business configuration value to manage object mapping dynamically. The following steps illustrate how this configuration influences data mapping and table creation:

Using Business Configuration for Data Insertion

When the insertion business configuration value is set to true, we utilize the mapping object to ensure data is returned and stored in a specific sequence. If insertion is false, we revert to the old data mapping approach, directly returning the data without the additional mapping. Mapping Metadata List from Backend API

The metadata list from the backend API is mapped with our custom mapping object. This allows us to create tables using interface keys in Pascal case format.

Here’s an example mapping object for WorkRequestsDepartmentModel:

typescript Copy code export const workRequestDepartObject: { [key in keyof WorkRequestsDepartmentModel]: string } = { department: 'Department', departmentId: 'DepartmentId', departmentDesc: 'DepartmentDesc', }; With this mapping object, table columns will be created using Pascal case(keys).

Data Insertion Process

During data insertion, we map values according to the specified sequence before inserting them into the table. This ensures the data is stored in an ordered and structured format. eg: In work request department service async storeData(db: SQLiteObject, data: any, tableName: string): Promise {

const itemsList: WorkRequestsDepartmentModel[] = this.getDataWithMapping(data, true);
try {
  await this.sqliteService.insertToTable(tableName, itemsList, db);

  const updatedData: WorkRequestsDepartmentModel[] = await this.getWorkRequestData(); // Updated to expect an array
  this.addToWorkRequestsDepartment(updatedData);

  // Update local storage
  return await this.storage.set(TableNames4SQLiteDB.WORK_REQUESTS_DEPARTMENT, updatedData);

} catch (error) {
  console.error(error);
  return [];
}

}

Data Fetching will be decide based on business config value

private getDataWithMapping(data, isInsertion = false): WorkRequestsDepartmentModel[] { if (!BUSINESS_CONFIG.useSequenceMappingMethod) { return data.map((item: any): WorkRequestsDepartmentModel => ({ department: item.Department, departmentId: item.DepartmentId, departmentDesc: item.DepartmentDesc, })); }

if (BUSINESS_CONFIG.useSequenceMappingMethod && isInsertion) {
  // While inserting, we need mapped data
  return this.sharedService.getDataMapWithSequence(data, workRequestDepartObject);
}

if (BUSINESS_CONFIG.useSequenceMappingMethod) {
  // While fetching, return the data as is
  return data;
}

}

Tranlsation message by passing dynamic values.

/**

  • Retrieves a translated text for the given key and substitutes parameters into the translation string.
  • @param {string} key - The translation key to look up in the translation files.
  • @param {object} params - An object containing the parameters to substitute into the translation string.
  • @returns {Promise<string | unknown>} A promise that resolves to the translated text with parameters substituted,
  • or the key itself as a fallback if an error occurs.
  • @throws Will log an error to the console if the translation process fails. */ async getTranslatedTextWithParams(key: string, params: object): Promise<string | unknown> { try { const translatedText = await firstValueFrom(this.translate.instant(key, params)); return translatedText; } catch (error) { console.error(Error translating key "${key}":, error); return key; // Fallback to key } }

EX : { "welcome_message": "Hello, {{name}}! Welcome to {{appName}}.", "error_message": "An error occurred. Please try again." }

Use case : const key = 'welcome_message'; const params = { name: 'John', appName: 'MyApp' };

this.getTranslatedTextWithParams(key, params).then((result) => { console.log(result); // Output: "Hello, John! Welcome to MyApp." });

How It Works: Translation Lookup:

The function calls this.translate.instant to fetch the translated text for the specified key from the localization file. Parameter Substitution:

The placeholders in the translation string (e.g., {{name}}) are replaced by the corresponding values in the params object.


Keystore configuration for Android Release builds

The keystore configuration for Android release builds is as follows:

  • keyAlias: mam-maintenance
  • storeFile: mam-maintenance.jks
  • storePassword: Propel@1234
  • keyPassword: Propel@1234