<commit_before>/*
    Copyright (c) 2015, SerGIS Project Contributors. All rights reserved.
    Use of this source code is governed by the MIT License, which can be found
    in the LICENSE.txt file.
*/

// our modules
var config = require("../../config");

// The Game model (created in the `module.exports` function below)
var Game;

// Create the Game model
module.exports = function (mongoose) {
    var Schema = mongoose.Schema;

    // Game schema
    var gameSchema = new Schema({
        // The game owner
        owner: {
            type: Schema.Types.ObjectId,
            ref: "User",
            required: true
        },

        // The game name
        name: {
            type: String,
            required: true
        },

        // The game name (lowercase)
        name_lowercase: {
            type: String,
            required: true,
            unique: true,
            validator: function (value) {
                return config.URL_SAFE_REGEX.test(value);
            }
        },

        // The access level
        access: {
            type: String,
            enum: ["public", "organization", "private"],
            default: "private"
        },

        // The JSON data
        jsondata: {
            type: Schema.Types.Mixed,
            select: false
        },

        // The date that it was created
        created: {
            type: Date,
            default: Date.now
        }
    });

    // Game model instance method
    /**
     * Determine whether the game is accessible by a certain user.
     *
     * @param {?User} user - The user to check, if any.
     *
     * @return {boolean} Whether they are allowed to access.
     */
    gameSchema.methods.isAccessibleByUser = function (user) {
        var game = this;

        // If there's no user, then the game must be public for the person to be able to access
        if (!user) {
            return game.access == "public";
        }

        // If there's a user, then check the game's 3 possible access cases
        if (game.access == "public") {
            // Game is public; anyone can access
            return true;
        }
        var isInOrganization = user.organization && game.owner.organization &&
            game.owner.organization.equals(user.organization);
        if (game.access == "organization" && (user.isFullAdmin || isInOrganization)) {
            // Game is organization access; the user is in the owner's organization or the user is a full admin
            return true;
        }
        // (we don't know if game.owner is populated or now)
        if ((game.owner._id || game.owner).equals(user._id) || user.isFullAdmin || (user.isOrganizationAdmin && isInOrganization)) {
            // Game is private; the user is the owner or an admin
            return true;
        }

        // If we're still here, the user didn't have access
        return false;
    };

    // Game model static method
    /**
     * Get all the games, optionally filtered by an owner and/or access level.
     *
     * @param {User} [owner] - The game owner to filter by.
     * @param {Organization} [organization] - The organization to filter by
     *        (will only include games whose owner is part of the organization).
     * @param {string} [access] - The access level to filter with (will only
     *        include games with this exact access level).
     *
     * @return {Promise.<Array.<Game>>} The games that match this criteria.
     */
    gameSchema.statics.getAll = function (owner, organization, access) {
        var criteria = {};
        if (owner) criteria.owner = owner;
        if (access) criteria.access = access;
        return Promise.resolve(Game.find(criteria).populate("owner").exec()).then(function (games) {
            if (!organization) {
                // We don't have to check organization; we're done
                return games;
            }

            // Check each game owner's organization
            return games.filter(function (game) {
                return game.owner.organization && game.owner.organization.equals(organization);
            });
        });
    };

    // Game model
    return (Game = mongoose.model("Game", gameSchema));
};
<commit_msg>Fixed small issue<commit_after>/*
    Copyright (c) 2015, SerGIS Project Contributors. All rights reserved.
    Use of this source code is governed by the MIT License, which can be found
    in the LICENSE.txt file.
*/

// our modules
var config = require("../../config");

// The Game model (created in the `module.exports` function below)
var Game;

// Create the Game model
module.exports = function (mongoose) {
    var Schema = mongoose.Schema;

    // Game schema
    var gameSchema = new Schema({
        // The game owner
        owner: {
            type: Schema.Types.ObjectId,
            ref: "User",
            required: true
        },

        // The game name
        name: {
            type: String,
            required: true
        },

        // The game name (lowercase)
        name_lowercase: {
            type: String,
            required: true,
            unique: true,
            validator: function (value) {
                return config.URL_SAFE_REGEX.test(value);
            }
        },

        // The access level
        access: {
            type: String,
            enum: ["public", "organization", "private"],
            default: "private"
        },

        // The JSON data
        jsondata: {
            type: Schema.Types.Mixed,
            select: false
        },

        // The date that it was created
        created: {
            type: Date,
            default: Date.now
        }
    });

    // Game model instance method
    /**
     * Determine whether the game is accessible by a certain user.
     *
     * @param {?User} user - The user to check, if any.
     *
     * @return {boolean} Whether they are allowed to access.
     */
    gameSchema.methods.isAccessibleByUser = function (user) {
        var game = this;

        // If there's no user, then the game must be public for the person to be able to access
        if (!user) {
            return game.access == "public";
        }

        // If there's a user, then check the game's 3 possible access cases
        if (game.access == "public") {
            // Game is public; anyone can access
            return true;
        }
        var isInOrganization = user.organization && game.owner.organization &&
            game.owner.organization.equals(user.organization);
        if (game.access == "organization" && (user.isFullAdmin || isInOrganization)) {
            // Game is organization access; the user is in the owner's organization or the user is a full admin
            return true;
        }
        // (we don't know if game.owner is populated or now)
        if ((game.owner._id || game.owner).equals(user._id) || user.isFullAdmin || (user.isOrganizationAdmin && isInOrganization)) {
            // Game is private; the user is the owner or an admin
            return true;
        }

        // If we're still here, the user didn't have access
        return false;
    };

    // Game model static method
    /**
     * Get all the games, optionally filtered by an owner and/or access level.
     *
     * @param {User} [owner] - The game owner to filter by.
     * @param {Organization} [organization] - The organization to filter by
     *        (will only include games whose owner is part of the organization).
     * @param {string} [access] - The access level to filter with (will only
     *        include games with this exact access level).
     *
     * @return {Promise.<Array.<Game>>} The games that match this criteria.
     */
    gameSchema.statics.getAll = function (owner, organization, access) {
        var criteria = {};
        if (owner) criteria.owner = owner;
        if (access) criteria.access = access;
        return Promise.resolve(Game.find(criteria).populate("owner").exec()).then(function (games) {
            if (!organization) {
                // We don't have to check organization; we're done
                return games;
            }

            // Check each game owner's organization
            return games.filter(function (game) {
                return game.owner.organization && game.owner.organization.equals(organization._id);
            });
        });
    };

    // Game model
    return (Game = mongoose.model("Game", gameSchema));
};
<|endoftext|>


<commit_before>var timespan = require('timespan')
  , S = require('string')
  , util = require('util')
  , ms = require('ms')

function Situation (outStream) {
  this.outStream = outStream || process.stdout
  this.vars = []
  this.funcs = {}
  this.displayVars = {startTime: 'Start Time', runTime: 'Run Time'}
  this.eval = null
  this.startTime = null
  this.shouldOutputJSON = false
  this.interval = null

  this.__defineGetter__('evalString', function() {
    var s = '(function() { return { \n'
    this.vars.forEach(function(key) {
      var fns = '  "key' + key + '": function() { return ' + key + '; },\n'
      s += fns
    })
    s = s.substring(0, s.length - 2) //chop last ',\n'
    s += ' } } )()'
    return s
  })
}

Situation.prototype.watch = function() {
  if (arguments.length === 1) {
    this.vars.push(arguments[0])
  }
  else if (arguments.length === 2) {
    if (typeof arguments[1] === 'string') {
      this.displayVars[arguments[1]] = arguments[0]
      this.vars.push(arguments[1])
    } else if (typeof arguments[1] === 'function') {
      var name = S(arguments[0].toLowerCase()).slugify().s
      this.displayVars[name] = arguments[0]
      this.funcs[name] = arguments[1]
    }
  }

  return this
}

Situation.prototype.update = function() {
  var uobj = {}
  var _this = this

  if (!this.eval) {
    console.error("No situationObj.eval object. Did you forget to run eval(situationObj.evalString)?")
    return
  }

  if (!this.startTime)
    this.startTime = new Date()

  uobj['startTime'] = this.startTime.toISOString()
  var ts = timespan.fromDates(this.startTime, new Date())
  var runtime = util.format("%d:%d:%d", ts.hours, ts.minutes, ts.seconds)
  uobj['runTime'] = runtime

  this.vars.forEach(function(val) {
    uobj[val] = _this.eval['key' + val]() || 'undefined (typo?)'
  })

  Object.keys(this.funcs).forEach(function(key) {
    var func = _this.funcs[key]
    uobj[key] = func()
  })

  var output = formatOutput.call(this, uobj)
  this.outStream.write(output)
}

Situation.prototype.start = function(updateInterval) {
  var _this = this
    , updateIntervalMillis = null

  if (updateInterval) {
    if (typeof updateInterval === 'number')
      updateIntervalMillis = updateInterval
    else if (typeof updateInterval === 'string')
      updateIntervalMillis = ms(updateInterval)
  }

  this.startTime = new Date()

  if (updateIntervalMillis)
    this.interval = setInterval(function() { _this.update() }, updateIntervalMillis)

  return this;
}

Situation.prototype.stop = function() {
  if (this.interval) {
    clearInterval(this.interval)
    this.interval = null
  }
}

module.exports = function(outStream) {
  return new Situation(outStream)
}

function formatOutput (dataObj) {
  var _this = this
    , maxLeftLen = -1
    , maxRightLen = -1

  if (this.shouldOutputJSON) {
    return JSON.stringify(dataObj, null, 2)
  } else {
    var displayBody = []
    var keys = Object.keys(dataObj)

    //calc lengths and display keys
    keys.forEach(function(key, i) {
      var displayKey = _this.displayVars[key] || key
      maxLeftLen = displayKey.length > maxLeftLen ? displayKey.length : maxLeftLen

      var val = dataObj[key]
      maxRightLen = val.length > maxRightLen ? val.length : maxRightLen
    })

    var totalLen = 2 + maxLeftLen + 2 + maxRightLen + 2
    var frameLine = S('*').times(totalLen).s + '\n'
    var buff = ''
    keys.forEach(function(key) {
      var displayKey = _this.displayVars[key] || key
        , val = dataObj[key]

      buff += '* ' + S(displayKey).padLeft(maxLeftLen).s + ': ' + S(val).padRight(maxRightLen).s + ' *\n'
    })

    return "\n" + frameLine + buff + frameLine + "\n"
  }
}

<commit_msg>Fixed bug.<commit_after>var timespan = require('timespan')
  , S = require('string')
  , util = require('util')
  , ms = require('ms')

function Situation (outStream) {
  this.outStream = outStream || process.stdout
  this.vars = []
  this.funcs = {}
  this.displayVars = {startTime: 'Start Time', runTime: 'Run Time'}
  this.eval = null
  this.startTime = null
  this.shouldOutputJSON = false
  this.interval = null

  this.__defineGetter__('evalString', function() {
    var s = '(function() { return { \n'
    this.vars.forEach(function(key) {
      var fns = '  "key' + key + '": function() { return ' + key + '; },\n'
      s += fns
    })
    s = s.substring(0, s.length - 2) //chop last ',\n'
    s += ' } } )()'
    return s
  })
}

Situation.prototype.watch = function() {
  if (arguments.length === 1) {
    this.vars.push(arguments[0])
  }
  else if (arguments.length === 2) {
    if (typeof arguments[1] === 'string') {
      this.displayVars[arguments[1]] = arguments[0]
      this.vars.push(arguments[1])
    } else if (typeof arguments[1] === 'function') {
      var name = S(arguments[0].toLowerCase()).slugify().s
      this.displayVars[name] = arguments[0]
      this.funcs[name] = arguments[1]
    }
  }

  return this
}

Situation.prototype.update = function() {
  var uobj = {}
  var _this = this

  if (!this.eval) {
    console.error("No situationObj.eval object. Did you forget to run eval(situationObj.evalString)?")
    return
  }

  if (!this.startTime)
    this.startTime = new Date()

  uobj['startTime'] = this.startTime.toISOString()
  var ts = timespan.fromDates(this.startTime, new Date())
  var runtime = util.format("%d:%d:%d", ts.hours, ts.minutes, ts.seconds)
  uobj['runTime'] = runtime

  this.vars.forEach(function(val) {
    uobj[val] = _this.eval['key' + val]()
  })

  Object.keys(this.funcs).forEach(function(key) {
    var func = _this.funcs[key]
    uobj[key] = func()
  })

  var output = formatOutput.call(this, uobj)
  this.outStream.write(output)
}

Situation.prototype.start = function(updateInterval) {
  var _this = this
    , updateIntervalMillis = null

  if (updateInterval) {
    if (typeof updateInterval === 'number')
      updateIntervalMillis = updateInterval
    else if (typeof updateInterval === 'string')
      updateIntervalMillis = ms(updateInterval)
  }

  this.startTime = new Date()

  if (updateIntervalMillis)
    this.interval = setInterval(function() { _this.update() }, updateIntervalMillis)

  return this;
}

Situation.prototype.stop = function() {
  if (this.interval) {
    clearInterval(this.interval)
    this.interval = null
  }
}

module.exports = function(outStream) {
  return new Situation(outStream)
}

function formatOutput (dataObj) {
  var _this = this
    , maxLeftLen = -1
    , maxRightLen = -1

  if (this.shouldOutputJSON) {
    return JSON.stringify(dataObj, null, 2)
  } else {
    var displayBody = []
    var keys = Object.keys(dataObj)

    //calc lengths and display keys
    keys.forEach(function(key, i) {
      var displayKey = _this.displayVars[key] || key
      maxLeftLen = displayKey.length > maxLeftLen ? displayKey.length : maxLeftLen

      var val = dataObj[key]
      maxRightLen = val.length > maxRightLen ? val.length : maxRightLen
    })

    var totalLen = 2 + maxLeftLen + 2 + maxRightLen + 2
    var frameLine = S('*').times(totalLen).s + '\n'
    var buff = ''
    keys.forEach(function(key) {
      var displayKey = _this.displayVars[key] || key
        , val = dataObj[key]

      buff += '* ' + S(displayKey).padLeft(maxLeftLen).s + ': ' + S(val).padRight(maxRightLen).s + ' *\n'
    })

    return "\n" + frameLine + buff + frameLine + "\n"
  }
}

<|endoftext|>


<commit_before>import React from 'react';
import { translate } from 'react-i18next';
import SearchIcon from '@material-ui/icons/Search';
import CloseRoundedIcon from '@material-ui/icons/CloseRounded';
import DeleteForeverRoundedIcon from '@material-ui/icons/DeleteForeverRounded';
import EditRoundedIcon from '@material-ui/icons/EditRounded';
import KeyboardArrowDownRoundedIcon from '@material-ui/icons/KeyboardArrowDownRounded';
import OpenInNewRoundedIcon from '@material-ui/icons/OpenInNewRounded';
import VisibilityRoundedIcon from '@material-ui/icons/VisibilityRounded';
import { Box, Flex } from 'rebass/styled-components';
import map from 'lodash/map';
import filter from 'lodash/filter';
import get from 'lodash/get';
import includes from 'lodash/includes';
import keyBy from 'lodash/keyBy';
import keys from 'lodash/keys';
import noop from 'lodash/noop';
import reduce from 'lodash/reduce';
import transform from 'lodash/transform';
import values from 'lodash/values';
import without from 'lodash/without';

import {
  bindPopover,
  bindTrigger,
  usePopupState,
} from 'material-ui-popup-state/hooks';

import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
} from '../../components/elements/Dialog';

import Button from '../../components/elements/Button';
import Checkbox from '../../components/elements/Checkbox';
import Pill from '../../components/elements/Pill';
import Popover from '../../components/elements/Popover';
import PopoverMenu from '../../components/elements/PopoverMenu';
import Table from '../../components/elements/Table';
import TextInput from '../../components/elements/TextInput';
import ClinicProfile from '../../components/clinic/ClinicProfile';
import { Body1, Headline, MediumTitle } from '../../components/elements/FontStyles';
import withPrescriptions from './withPrescriptions';
import withAssociatedAccounts from './withAssociatedAccounts';
import { dateRegex, prescriptionStateOptions } from './prescriptionFormConstants';
import { useToasts } from '../../providers/ToastProvider';
import { useIsFirstRender } from '../../core/hooks';

const Prescriptions = props => {
  const {
    t,
    deletePrescription,
    deletingPrescription,
    history,
    prescriptions = [],
    membershipPermissionsInOtherCareTeams = {},
  } = props;

  const isFirstRender = useIsFirstRender();
  const { set: setToast } = useToasts();
  const prescriptionStates = keyBy(prescriptionStateOptions, 'value');

  const popupFilterState = usePopupState({
    variant: 'popover',
    popupId: 'filterState',
  });

  const initialDeleteDialogState = {
    open: false,
    prescription: {},
  };

  const [deleteDialog, setDeleteDialog] = React.useState(initialDeleteDialogState);

  function closeDeleteDialog() {
    setDeleteDialog({
      ...deleteDialog,
      open: false,
    });

    // We fully reset the dialog after it's had time to close, so that we don't see the patient name
    // in the delete dialog disappear before it closes.
    setTimeout(() => setDeleteDialog(initialDeleteDialogState), 100);
  }

  const [searchText, setSearchText] = React.useState('');
  const [filterStateActive, setFilterStateActive] = React.useState(false);

  const [activeStates, setActiveStates] = React.useState(reduce(prescriptionStateOptions, (result, { value }) => {
    result[value] = true;
    return result;
  }, {}));

  const activeStatesCount = without(values(activeStates), false).length;

  const [pendingActiveStates, setPendingActiveStates] = React.useState(activeStates);
  const togglePendingActiveState = (value) => setPendingActiveStates({ ...pendingActiveStates, [value]: !pendingActiveStates[value] })

  const data = filter(
    map(prescriptions, prescription => ({
      birthday: get(prescription, 'latestRevision.attributes.birthday', '').replace(dateRegex, '$2/$3/$1'),
      createdTime: get(prescription, 'latestRevision.attributes.createdTime'),
      firstName: get(prescription, 'latestRevision.attributes.firstName'),
      id: get(prescription, 'id'),
      lastName: get(prescription, 'latestRevision.attributes.lastName'),
      mrn: get(prescription, 'latestRevision.attributes.mrn'),
      patientUserId: get(prescription, 'patientUserId'),
      state: get(prescription, 'state'),
    })),
    prescription => activeStates[prescription.state]
  );

  function handleSearchChange(event) {
    setSearchText(event.target.value);
  }

  function clearSearchText() {
    setSearchText('');
  }

  const handleAddNew = () => props.history.push('/prescriptions/new');

  const handleOpenPrescription = prescription => popupState => {
    props.history.push(`/prescriptions/${prescription.id}`);
  };

  const handleDeletePrescription = prescription => popupState => {
    popupState.close();
    setDeleteDialog({
      open: true,
      prescription,
    });
  }

  function handleRowClick(prescription) {
    return handleOpenPrescription(prescription)();
  }

  const actionMenuItems = prescription => {
    const isEditable = includes(['draft', 'pending'], prescription.state);

    const items = [
      {
        icon: isEditable ? EditRoundedIcon : VisibilityRoundedIcon,
        iconLabel: isEditable ? t('Update') : t('View'),
        iconPosition: 'left',
        id: isEditable ? 'update' : 'view',
        onClick: handleOpenPrescription(prescription),
        text: isEditable ? t('Update prescription') : t('View Prescription'),
        variant: 'actionListItem',
      },
    ];

    if (isEditable) items.push({
      icon: DeleteForeverRoundedIcon,
      iconLabel: 'Delete',
      iconPosition: 'left',
      id: 'delete',
      onClick: handleDeletePrescription(prescription),
      text: t('Delete prescription'),
      variant: 'actionListItemDanger',
      disabled: !isEditable,
    });

    return items;
  };

  const renderMore = (prescription) => (
    <Box onClick={e => {
      // Prevent clicks from propogating up to the table row click handlers
      e.stopPropagation();
    }}>
      <PopoverMenu
        id="more-prescription-actions"
        label="More actions"
        items={actionMenuItems(prescription)}
      />
    </Box>
  );

  const openPatientData = (patientUserId) => {
    const href = history.createHref({
      pathname: `/patients/${patientUserId}/data`,
    });

    window.open(href, '_blank');
  }

  const patientNameFromPrescription = ({ firstName = '', lastName = '' }) => [firstName, lastName].join(' ');

  const renderName = prescription => (
    <React.Fragment>
      {prescription.patientUserId && get(membershipPermissionsInOtherCareTeams, [prescription.patientUserId, 'view']) ? <Button
        p={0}
        m={0}
        color="text.link"
        fontSize={1}
        icon={OpenInNewRoundedIcon}
        iconLabel="Open patient data in new tab"
        variant="textPrimary"
        onClick={(e) => {
          // Prevent clicks from propogating up to the table row click handlers
          e.stopPropagation();
          openPatientData(prescription.patientUserId);
        }}
      >
        {patientNameFromPrescription(prescription)}
      </Button> : patientNameFromPrescription(prescription)
     }
    </React.Fragment>
  );

  const renderState = ({ state }) => {
    const label = get(prescriptionStates, [state, 'label'], '');
    const colorPalette = get(prescriptionStates, [state, 'colorPalette'])
    return <Pill label="prescription status" colorPalette={colorPalette} text={label} />
  }

  const columns = [
    { title: t('Name'), field: 'patient', align: 'left', sortable: true, sortBy: 'firstName', render: renderName, searchable: true, searchBy: ['firstName', 'lastName'] },
    { title: t('MRN'), field: 'mrn', align: 'left', sortable: true, searchable: true },
    { title: t('Date of birth'), field: 'birthday', align: 'left', sortable: true, searchable: true },
    { title: t('Status'), field: 'state', render: renderState, align: 'left', sortable: true },
    { title: '', field: 'more', render: renderMore, align: 'left' },
  ];

  // Handle successful or failed deletion attempts
  React.useEffect(() => {
    const { inProgress, completed, notification } = deletingPrescription;

    if (!isFirstRender && !inProgress) {
      if (completed) {
        if (deleteDialog.open) {
          closeDeleteDialog();
        }

        setToast({
          message: t('You have successfully {{messageAction}} a Tidepool Loop prescription.', { messageAction: 'deleted' }),
          variant: 'success',
        });
      }

      if (completed === false) {
        setToast({
          message: get(notification, 'message'),
          variant: 'danger',
        });
      }
    }
  }, [deletingPrescription]);

  // Render
  return (
    <>
      <ClinicProfile />

      <Box
        variant="containers.largeBordered"
        px={4}
        pb={4}
      >
        <Flex py={4} justifyContent="space-between">
          <Box alignSelf="flex-end">
            <Flex>
              <Headline mr={3}>{t('Prescriptions')}</Headline>
              <Button variant="primary" onClick={handleAddNew}>{t('Add New')}</Button>
            </Flex>
          </Box>
          <Box>
            <Flex>
              <Button
                variant="filter"
                active={filterStateActive}
                {...bindTrigger(popupFilterState)}
                icon={KeyboardArrowDownRoundedIcon}
                iconLabel="Filter by status"
                mr={2}
                fontSize={1}
              >
                {t('Status{{count}}', {
                  count: activeStatesCount < keys(prescriptionStates).length ? ` (${activeStatesCount})` : '',
                })}
              </Button>

              <Popover width="15em" {...bindPopover(popupFilterState)}>
                <DialogContent px={2} py={3} dividers>
                  {map(prescriptionStateOptions, ({label, value}) => (
                    <Box>
                      <Checkbox
                        checked={pendingActiveStates[value]}
                        key={`filter-${value}`}
                        name={`filter-${value}`}
                        label={label}
                        onChange={() => togglePendingActiveState(value)}
                      />
                    </Box>
                  ))}
                </DialogContent>
                <DialogActions justifyContent="space-between" p={1}>
                  <Button
                    fontSize={1}
                    variant="textSecondary"
                    onClick={() => {
                      const active = without(values(pendingActiveStates), false).length < keys(prescriptionStates).length;

                      setPendingActiveStates(transform(pendingActiveStates, function(result, value, key) {
                        result[key] = active;
                      }, {}));
                    }}
                  >
                    {(without(values(pendingActiveStates), false).length < keys(prescriptionStates).length) ? t('Select All') : t('Deselect All')}
                  </Button>
                  <Button fontSize={0} variant="textPrimary" onClick={() => {
                    setActiveStates(pendingActiveStates);
                    setFilterStateActive(includes(values(pendingActiveStates), false));
                    popupFilterState.close();
                  }}>
                    {t('Apply')}
                  </Button>
                </DialogActions>
              </Popover>

              <TextInput
                themeProps={{
                  width: 'auto',
                  minWidth: '250px',
                }}
                placeholder={t('Search Entries')}
                icon={searchText ? CloseRoundedIcon : SearchIcon}
                iconLabel="search"
                onClickIcon={searchText ? clearSearchText : null}
                name="search-prescriptions"
                onChange={handleSearchChange}
                value={searchText}
                variant="condensed"
              />
            </Flex>
          </Box>
        </Flex>
        <Table
          fontSize={1}
          label="Prescription List"
          id="prescriptions-table"
          data={data}
          columns={columns}
          rowsPerPage={10}
          searchText={searchText}
          onClickRow={handleRowClick}
          orderBy="createdTime"
          order="desc"
          pagination
        />
        <Dialog
          id={'prescription-delete'}
          aria-labelledby="dialog-title"
          open={deleteDialog.open}
          onClose={closeDeleteDialog}
        >
          <DialogTitle onClose={closeDeleteDialog}>
            <MediumTitle mr={2} id="dialog-title">Delete Prescription for {patientNameFromPrescription(deleteDialog.prescription)}</MediumTitle>
          </DialogTitle>
          <DialogContent>
            <Body1>
              Are you sure you want to delete this prescription?
            </Body1>
          </DialogContent>
          <DialogActions>
            <Button variant="secondary" onClick={closeDeleteDialog}>
              Cancel
            </Button>
            <Button
              variant="danger"
              processing={deletingPrescription.inProgress}
              onClick={() => deletePrescription(deleteDialog.prescription.id)}
            >
              Delete Prescription
            </Button>
          </DialogActions>
        </Dialog>
      </Box>
    </>
  );
};

export default withPrescriptions(withAssociatedAccounts(translate()(Prescriptions)));
<commit_msg>[WEB-880] Remove ClinicProfile component from Prescriptions rendering<commit_after>import React from 'react';
import { translate } from 'react-i18next';
import SearchIcon from '@material-ui/icons/Search';
import CloseRoundedIcon from '@material-ui/icons/CloseRounded';
import DeleteForeverRoundedIcon from '@material-ui/icons/DeleteForeverRounded';
import EditRoundedIcon from '@material-ui/icons/EditRounded';
import KeyboardArrowDownRoundedIcon from '@material-ui/icons/KeyboardArrowDownRounded';
import OpenInNewRoundedIcon from '@material-ui/icons/OpenInNewRounded';
import VisibilityRoundedIcon from '@material-ui/icons/VisibilityRounded';
import { Box, Flex } from 'rebass/styled-components';
import map from 'lodash/map';
import filter from 'lodash/filter';
import get from 'lodash/get';
import includes from 'lodash/includes';
import keyBy from 'lodash/keyBy';
import keys from 'lodash/keys';
import noop from 'lodash/noop';
import reduce from 'lodash/reduce';
import transform from 'lodash/transform';
import values from 'lodash/values';
import without from 'lodash/without';

import {
  bindPopover,
  bindTrigger,
  usePopupState,
} from 'material-ui-popup-state/hooks';

import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
} from '../../components/elements/Dialog';

import Button from '../../components/elements/Button';
import Checkbox from '../../components/elements/Checkbox';
import Pill from '../../components/elements/Pill';
import Popover from '../../components/elements/Popover';
import PopoverMenu from '../../components/elements/PopoverMenu';
import Table from '../../components/elements/Table';
import TextInput from '../../components/elements/TextInput';
import ClinicProfile from '../../components/clinic/ClinicProfile';
import { Body1, Headline, MediumTitle } from '../../components/elements/FontStyles';
import withPrescriptions from './withPrescriptions';
import withAssociatedAccounts from './withAssociatedAccounts';
import { dateRegex, prescriptionStateOptions } from './prescriptionFormConstants';
import { useToasts } from '../../providers/ToastProvider';
import { useIsFirstRender } from '../../core/hooks';

const Prescriptions = props => {
  const {
    t,
    deletePrescription,
    deletingPrescription,
    history,
    prescriptions = [],
    membershipPermissionsInOtherCareTeams = {},
  } = props;

  const isFirstRender = useIsFirstRender();
  const { set: setToast } = useToasts();
  const prescriptionStates = keyBy(prescriptionStateOptions, 'value');

  const popupFilterState = usePopupState({
    variant: 'popover',
    popupId: 'filterState',
  });

  const initialDeleteDialogState = {
    open: false,
    prescription: {},
  };

  const [deleteDialog, setDeleteDialog] = React.useState(initialDeleteDialogState);

  function closeDeleteDialog() {
    setDeleteDialog({
      ...deleteDialog,
      open: false,
    });

    // We fully reset the dialog after it's had time to close, so that we don't see the patient name
    // in the delete dialog disappear before it closes.
    setTimeout(() => setDeleteDialog(initialDeleteDialogState), 100);
  }

  const [searchText, setSearchText] = React.useState('');
  const [filterStateActive, setFilterStateActive] = React.useState(false);

  const [activeStates, setActiveStates] = React.useState(reduce(prescriptionStateOptions, (result, { value }) => {
    result[value] = true;
    return result;
  }, {}));

  const activeStatesCount = without(values(activeStates), false).length;

  const [pendingActiveStates, setPendingActiveStates] = React.useState(activeStates);
  const togglePendingActiveState = (value) => setPendingActiveStates({ ...pendingActiveStates, [value]: !pendingActiveStates[value] })

  const data = filter(
    map(prescriptions, prescription => ({
      birthday: get(prescription, 'latestRevision.attributes.birthday', '').replace(dateRegex, '$2/$3/$1'),
      createdTime: get(prescription, 'latestRevision.attributes.createdTime'),
      firstName: get(prescription, 'latestRevision.attributes.firstName'),
      id: get(prescription, 'id'),
      lastName: get(prescription, 'latestRevision.attributes.lastName'),
      mrn: get(prescription, 'latestRevision.attributes.mrn'),
      patientUserId: get(prescription, 'patientUserId'),
      state: get(prescription, 'state'),
    })),
    prescription => activeStates[prescription.state]
  );

  function handleSearchChange(event) {
    setSearchText(event.target.value);
  }

  function clearSearchText() {
    setSearchText('');
  }

  const handleAddNew = () => props.history.push('/prescriptions/new');

  const handleOpenPrescription = prescription => popupState => {
    props.history.push(`/prescriptions/${prescription.id}`);
  };

  const handleDeletePrescription = prescription => popupState => {
    popupState.close();
    setDeleteDialog({
      open: true,
      prescription,
    });
  }

  function handleRowClick(prescription) {
    return handleOpenPrescription(prescription)();
  }

  const actionMenuItems = prescription => {
    const isEditable = includes(['draft', 'pending'], prescription.state);

    const items = [
      {
        icon: isEditable ? EditRoundedIcon : VisibilityRoundedIcon,
        iconLabel: isEditable ? t('Update') : t('View'),
        iconPosition: 'left',
        id: isEditable ? 'update' : 'view',
        onClick: handleOpenPrescription(prescription),
        text: isEditable ? t('Update prescription') : t('View Prescription'),
        variant: 'actionListItem',
      },
    ];

    if (isEditable) items.push({
      icon: DeleteForeverRoundedIcon,
      iconLabel: 'Delete',
      iconPosition: 'left',
      id: 'delete',
      onClick: handleDeletePrescription(prescription),
      text: t('Delete prescription'),
      variant: 'actionListItemDanger',
      disabled: !isEditable,
    });

    return items;
  };

  const renderMore = (prescription) => (
    <Box onClick={e => {
      // Prevent clicks from propogating up to the table row click handlers
      e.stopPropagation();
    }}>
      <PopoverMenu
        id="more-prescription-actions"
        label="More actions"
        items={actionMenuItems(prescription)}
      />
    </Box>
  );

  const openPatientData = (patientUserId) => {
    const href = history.createHref({
      pathname: `/patients/${patientUserId}/data`,
    });

    window.open(href, '_blank');
  }

  const patientNameFromPrescription = ({ firstName = '', lastName = '' }) => [firstName, lastName].join(' ');

  const renderName = prescription => (
    <React.Fragment>
      {prescription.patientUserId && get(membershipPermissionsInOtherCareTeams, [prescription.patientUserId, 'view']) ? <Button
        p={0}
        m={0}
        color="text.link"
        fontSize={1}
        icon={OpenInNewRoundedIcon}
        iconLabel="Open patient data in new tab"
        variant="textPrimary"
        onClick={(e) => {
          // Prevent clicks from propogating up to the table row click handlers
          e.stopPropagation();
          openPatientData(prescription.patientUserId);
        }}
      >
        {patientNameFromPrescription(prescription)}
      </Button> : patientNameFromPrescription(prescription)
     }
    </React.Fragment>
  );

  const renderState = ({ state }) => {
    const label = get(prescriptionStates, [state, 'label'], '');
    const colorPalette = get(prescriptionStates, [state, 'colorPalette'])
    return <Pill label="prescription status" colorPalette={colorPalette} text={label} />
  }

  const columns = [
    { title: t('Name'), field: 'patient', align: 'left', sortable: true, sortBy: 'firstName', render: renderName, searchable: true, searchBy: ['firstName', 'lastName'] },
    { title: t('MRN'), field: 'mrn', align: 'left', sortable: true, searchable: true },
    { title: t('Date of birth'), field: 'birthday', align: 'left', sortable: true, searchable: true },
    { title: t('Status'), field: 'state', render: renderState, align: 'left', sortable: true },
    { title: '', field: 'more', render: renderMore, align: 'left' },
  ];

  // Handle successful or failed deletion attempts
  React.useEffect(() => {
    const { inProgress, completed, notification } = deletingPrescription;

    if (!isFirstRender && !inProgress) {
      if (completed) {
        if (deleteDialog.open) {
          closeDeleteDialog();
        }

        setToast({
          message: t('You have successfully {{messageAction}} a Tidepool Loop prescription.', { messageAction: 'deleted' }),
          variant: 'success',
        });
      }

      if (completed === false) {
        setToast({
          message: get(notification, 'message'),
          variant: 'danger',
        });
      }
    }
  }, [deletingPrescription]);

  // Render
  return (
    <Box
      variant="containers.largeBordered"
      px={4}
      pb={4}
    >
      <Flex py={4} justifyContent="space-between">
        <Box alignSelf="flex-end">
          <Flex>
            <Headline mr={3}>{t('Prescriptions')}</Headline>
            <Button variant="primary" onClick={handleAddNew}>{t('Add New')}</Button>
          </Flex>
        </Box>
        <Box>
          <Flex>
            <Button
              variant="filter"
              active={filterStateActive}
              {...bindTrigger(popupFilterState)}
              icon={KeyboardArrowDownRoundedIcon}
              iconLabel="Filter by status"
              mr={2}
              fontSize={1}
            >
              {t('Status{{count}}', {
                count: activeStatesCount < keys(prescriptionStates).length ? ` (${activeStatesCount})` : '',
              })}
            </Button>

            <Popover width="15em" {...bindPopover(popupFilterState)}>
              <DialogContent px={2} py={3} dividers>
                {map(prescriptionStateOptions, ({label, value}) => (
                  <Box>
                    <Checkbox
                      checked={pendingActiveStates[value]}
                      key={`filter-${value}`}
                      name={`filter-${value}`}
                      label={label}
                      onChange={() => togglePendingActiveState(value)}
                    />
                  </Box>
                ))}
              </DialogContent>
              <DialogActions justifyContent="space-between" p={1}>
                <Button
                  fontSize={1}
                  variant="textSecondary"
                  onClick={() => {
                    const active = without(values(pendingActiveStates), false).length < keys(prescriptionStates).length;

                    setPendingActiveStates(transform(pendingActiveStates, function(result, value, key) {
                      result[key] = active;
                    }, {}));
                  }}
                >
                  {(without(values(pendingActiveStates), false).length < keys(prescriptionStates).length) ? t('Select All') : t('Deselect All')}
                </Button>
                <Button fontSize={0} variant="textPrimary" onClick={() => {
                  setActiveStates(pendingActiveStates);
                  setFilterStateActive(includes(values(pendingActiveStates), false));
                  popupFilterState.close();
                }}>
                  {t('Apply')}
                </Button>
              </DialogActions>
            </Popover>

            <TextInput
              themeProps={{
                width: 'auto',
                minWidth: '250px',
              }}
              placeholder={t('Search Entries')}
              icon={searchText ? CloseRoundedIcon : SearchIcon}
              iconLabel="search"
              onClickIcon={searchText ? clearSearchText : null}
              name="search-prescriptions"
              onChange={handleSearchChange}
              value={searchText}
              variant="condensed"
            />
          </Flex>
        </Box>
      </Flex>
      <Table
        fontSize={1}
        label="Prescription List"
        id="prescriptions-table"
        data={data}
        columns={columns}
        rowsPerPage={10}
        searchText={searchText}
        onClickRow={handleRowClick}
        orderBy="createdTime"
        order="desc"
        pagination
      />
      <Dialog
        id={'prescription-delete'}
        aria-labelledby="dialog-title"
        open={deleteDialog.open}
        onClose={closeDeleteDialog}
      >
        <DialogTitle onClose={closeDeleteDialog}>
          <MediumTitle mr={2} id="dialog-title">Delete Prescription for {patientNameFromPrescription(deleteDialog.prescription)}</MediumTitle>
        </DialogTitle>
        <DialogContent>
          <Body1>
            Are you sure you want to delete this prescription?
          </Body1>
        </DialogContent>
        <DialogActions>
          <Button variant="secondary" onClick={closeDeleteDialog}>
            Cancel
          </Button>
          <Button
            variant="danger"
            processing={deletingPrescription.inProgress}
            onClick={() => deletePrescription(deleteDialog.prescription.id)}
          >
            Delete Prescription
          </Button>
        </DialogActions>
      </Dialog>
    </Box>
  );
};

export default withPrescriptions(withAssociatedAccounts(translate()(Prescriptions)));
<|endoftext|>


<commit_before>'use babel';

import { CompositeDisposable } from 'atom';

export default {
  subscriptions: null,

  activate(state) {

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'slickwrap:toggle': () => this.toggle()
    }));
  },

  deactivate() {
    this.subscriptions.dispose();
  },

  toggle() {
    var editor = atom.workspace.getActiveTextEditor();
    var text = editor.getText();
    var lines = text.split(/\r?\n/);
    var endColumn = 80

    var resultLines = [];
    lines.map(function(line) {
      resultLines.push.apply(resultLines, adjusted(line, endColumn));
    });

    console.log(resultLines);
  },

};

function adjusted(line, limit) {
  var resultLines = [];
  if (line && line.length > limit) {
    var currentLength = 0;
    var currentLine = "";
    var lineRemaining = line;
    while (true) {
      var ws = lineRemaining.search(/\s/);
      currentLength += (ws + 1);
      if (currentLength > limit) {
        break;
      }
      currentLine += line.substring(0, ws + 1);
      lineRemaining = line.substring(ws + 1);
    }
    resultLines.push(currentLine)
    resultLines.push.apply(resultLines, adjusted(lineRemaining, limit));
  }
  else {
    resultLines.push(line);
  }
  return resultLines;
}
<commit_msg>update remaining line correctly<commit_after>'use babel';

import { CompositeDisposable } from 'atom';

export default {
  subscriptions: null,

  activate(state) {

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'slickwrap:toggle': () => this.toggle()
    }));
  },

  deactivate() {
    this.subscriptions.dispose();
  },

  toggle() {
    var editor = atom.workspace.getActiveTextEditor();
    var text = editor.getText();
    var lines = text.split(/\r?\n/);
    var endColumn = 80

    var resultLines = [];
    lines.map(function(line) {
      resultLines.push.apply(resultLines, adjusted(line, endColumn));
    });

    console.log(resultLines);
  },

};

function adjusted(line, limit) {
  console.log('input');
  console.log(line);
  var resultLines = [];
  if (line && line.length > limit) {
    var currentLength = 0;
    var currentLine = "";
    var lineRemaining = line;
    while (true) {
      var ws = lineRemaining.search(/\s/);
      console.log('ws: ' + ws);
      currentLength += (ws + 1);
      if (currentLength > limit) {
        break;
      }
      currentLine += lineRemaining.substring(0, ws + 1);
      lineRemaining = lineRemaining.substring(ws + 1);
    }
    resultLines.push(currentLine)
    resultLines.push.apply(resultLines, adjusted(lineRemaining, limit));
  }
  else {
    resultLines.push(line);
  }
  console.log('result');
  console.log(resultLines);
  return resultLines;
}
<|endoftext|>


<commit_before>'use strict';

angular.module('confRegistrationWebApp')
  .controller('paymentModal', function ($scope, $modalInstance, modalMessage, $http, registration, conference, permissions, permissionConstants) {
    $scope.registration = registration;
    $scope.conference = conference;
    $scope.processing = false;
    $scope.activeTab = {};
    $scope.newTransaction = {
      registrationId: registration.id,
      amount: 0,
      sendEmailReceipt: false
    };
    var permissionRequiredMsg = 'You do not have permission to perform this action. Please contact an event administrator to request permission.';

    $scope.getRegistrantType = function(id) {
      return _.find(conference.registrantTypes, { 'id': id });
    };

    $scope.getBlock = function (blockId) {
      var allBlocks = [];
      _.each(conference.registrationPages, function (page) {
        allBlocks = allBlocks.concat(page.blocks);
      });
      return _.find(allBlocks, {id: blockId});
    };

    $scope.processTransaction = function () {
      if (_.isEmpty($scope.newTransaction.paymentType)) {
        modalMessage.error('Please select a transaction type.');
        return;
      }
      if (Number($scope.newTransaction.amount) <= 0 && $scope.newTransaction.paymentType !== 'CASH') {
        $scope.newTransaction.errors.unshift('Transaction amount must be a positive number.');
      }

      if (!_.isEmpty($scope.newTransaction.errors)) {
        modalMessage.error({
          'title': 'Please correct the following errors:',
          'message': $scope.newTransaction.errors
        });
        return;
      }

      if(permissions.permissionInt < permissionConstants.UPDATE){
        if(permissions.permissionInt === permissionConstants.SCHOLARSHIP) {
          if($scope.newTransaction.paymentType !== 'SCHOLARSHIP'){
            modalMessage.error('Your permission level only allows scholarship payments to be added. Please contact an event administrator to request permission.');
            return;
          }
        }else{
          modalMessage.error(permissionRequiredMsg);
          return;
        }
      }

      if(permissions.permissionInt < permissionConstants.FULL && _.contains(['ADDITIONAL_EXPENSE', 'DISCOUNT'], $scope.newTransaction.paymentType)){
        modalMessage.error(permissionRequiredMsg);
        return;
      }

      var transaction = angular.copy($scope.newTransaction);
      delete transaction.errors;
      transaction.amount = Number(transaction.amount);
      var path = 'payments?sendEmailReceipt=' + transaction.sendEmailReceipt;
      delete transaction.sendEmailReceipt;

      if(_.contains(['ADDITIONAL_EXPENSE', 'DISCOUNT'], transaction.paymentType)) {
        if(!transaction.description){
          modalMessage.error('Please enter a description.');
          return;
        }

        path = 'expenses';
        if(transaction.paymentType === 'DISCOUNT'){
          transaction.amount = Number(transaction.amount) * -1;
        }
        delete transaction.paymentType;
      } else {
        transaction.readyToProcess = true;
      }

      if(transaction.paymentType === 'SCHOLARSHIP') {
        transaction.status = 'APPROVED';
      }

      $scope.processing = true;
      if(transaction.paymentType === 'CREDIT_CARD'){
        $http.get('payments/ccp-client-encryption-key', {cache: true}).success(function(ccpClientEncryptionKey) {
          ccp.initialize(ccpClientEncryptionKey);
          transaction.creditCard.lastFourDigits = ccp.getAbbreviatedNumber(transaction.creditCard.number);
          transaction.creditCard.number = ccp.encrypt(transaction.creditCard.number);
          transaction.creditCard.cvvNumber = ccp.encrypt(transaction.creditCard.cvvNumber);
          postTransaction(path, transaction);
        }).error(function() {
          $scope.processing = false;
          modalMessage.error('An error occurred while requesting the ccp encryption key.');
        });
      }else{
        delete transaction.creditCard;
        postTransaction(path, transaction);
      }
    };

    var postTransaction = function(path, transaction){
      $http.post(path, transaction).success(function () {
        loadPayments();
        if(path === 'expenses'){
          $scope.activeTab[2] = true;
        }else{
          $scope.activeTab[1] = true;
        }
      }).error(function (data) {
        modalMessage.error('Transaction failed. ' + data.errorMessage);
        $scope.processing = false;
      });
    };

    $scope.canBeRefunded = function (payment) {
      return payment.paymentType !== 'REFUND' &&
        payment.paymentType !== 'TRANSFER' &&
        payment.paymentType !== 'SCHOLARSHIP' &&
        $scope.calculateRefundableAmount(payment) > 0;
    };

    $scope.calculateRefundableAmount = function (payment) {
      if(angular.isUndefined(payment)) {
        return 0;
      }
      var sum = payment.amount;
      _.each($scope.registration.pastPayments, function (prevRefund) {
        if (prevRefund.paymentType === 'REFUND' && prevRefund.refundedPaymentId === payment.id) {
          sum -= prevRefund.amount;
        }
      });
      return sum;
    };

    $scope.startRefund = function (payment) {
      if(permissions.permissionInt < permissionConstants.UPDATE){
        modalMessage.error(permissionRequiredMsg);
        return;
      }
      $scope.paymentToRefund = payment;

      $scope.refund = {
        amount: $scope.calculateRefundableAmount(payment),
        refundedPaymentId: payment.id,
        registrationId: payment.registrationId,
        paymentType: 'REFUND',
        refundChannel: payment.paymentType,
        readyToProcess: true
      };
    };

    $scope.processRefund = function () {
      if(!$scope.refund.refundChannel){
        modalMessage.error('Please select a refund method.');
        return;
      }

      $scope.processing = true;
      $http.post('payments/', $scope.refund).success(function () {
        $scope.refund = null;
        loadPayments();
      }).error(function () {
        modalMessage.error('Refund failed.');
      }).finally(function(){
        $scope.processing = false;
      });
    };

    $scope.cancelRefund = function () {
      delete $scope.refund;
    };

    $scope.removeExpense = function (expense) {
      if(permissions.permissionInt < permissionConstants.UPDATE){
        modalMessage.error(permissionRequiredMsg);
        return;
      }

      modalMessage.confirm({
        'title': 'Delete Expense?',
        'question': 'Are you sure you want to delete this expense?'
      }).then(function(){
        $http.delete('expenses/' + expense.id).success(function () {
          loadPayments();
        }).error(function () {
          modalMessage.error('An error occurred while deleting this expense.');
        });
      });
    };

    $scope.savePaymentEdits = function (payment) {
      if(payment.paymentType === 'SCHOLARSHIP'){
        payment.status = 'APPROVED';
      }
      if(payment.paymentType === 'CHECK'){
        payment.status = 'RECEIVED';
      }

      $http.put('payments/' + payment.id, payment).success(function() {
        loadPayments();
        delete $scope.editPayment;
      }).error(function(){
        modalMessage.error('Payment could not be saved. Please verify all required fields are filled in correctly.');
      });
    };

    $scope.openEditPaymentRow = function (payment) {
      if(angular.isDefined($scope.editPayment) && $scope.editPayment.id === payment.id) {
        delete $scope.editPayment;
      } else {
        if(permissions.permissionInt >= permissionConstants.UPDATE || (permissions.permissionInt === permissionConstants.SCHOLARSHIP && payment.paymentType === 'SCHOLARSHIP')){
          $scope.editPayment = angular.copy(payment);
        }else{
          modalMessage.error(permissionRequiredMsg);
        }
      }
    };

    $scope.saveExpenseEdits = function (expense) {
      $http.put('expenses/' + expense.id, expense).success(function() {
        loadPayments();
        delete $scope.editExpense;
      });
    };

    var loadPayments = function() {
      $http.get('registrations/' + $scope.registration.id).success(function (data) {
        $scope.registration = data;
        $scope.processing = false;

        $scope.newTransaction = {
          registrationId: registration.id,
          amount: 0,
          sendEmailReceipt: false
        };
      });
    };

    $scope.deletePayment = function (payment) {
      if(permissions.permissionInt >= permissionConstants.UPDATE || (permissions.permissionInt === permissionConstants.SCHOLARSHIP && payment.paymentType === 'SCHOLARSHIP')){
      }else{
        modalMessage.error(permissionRequiredMsg);
        return;
      }
      modalMessage.confirm({
        'title':'Delete payment?',
        'question': 'Are you sure you want to delete this payment?'
      }).then(function(){
        $http.delete('payments/' + payment.id, payment).success(function () {
          loadPayments();
        }).error(function () {
          modalMessage.error('An error occurred while deleting this payment.');
        });
      });
    };

    $scope.openEditExpenseRow = function (expense) {
      if(angular.isDefined($scope.editExpense) && $scope.editExpense.id === expense.id) {
        delete $scope.editExpense;
      } else {
        if(permissions.permissionInt < permissionConstants.UPDATE){
          modalMessage.error(permissionRequiredMsg);
          return;
        }

        $scope.editExpense = angular.copy(expense);
      }
    };

    $scope.allowCreditCardPayments = function () {
      var allow = false;
      _.each($scope.registration.registrants, function(registrant) {
        var registrantType = $scope.getRegistrantType(registrant.registrantTypeId);
        if(registrantType.acceptCreditCards) {
          allow = true;
        }
      });

      return allow;
    };

    //auto set new transaction amount
    $scope.setTransactionAmount = function(){
      var paymentTypes = ['CREDIT_CARD', 'OFFLINE_CREDIT_CARD', 'SCHOLARSHIP', 'TRANSFER', 'CHECK', 'CASH'];

      if($scope.registration.remainingBalance > 0 && ($scope.newTransaction.amount === 0 || $scope.newTransaction.amount === '' || $scope.newTransaction.amount === '0') && _.contains(paymentTypes, $scope.newTransaction.paymentType)){
        $scope.newTransaction.amount = $scope.registration.remainingBalance;
      }
    };

    $scope.addPromotion = function(inputCode){
      $http.post('registrations/' + registration.id + '/promotions', {code: inputCode}).success(function () {
        loadPayments();
      }).error(function (data, status) {
        modalMessage.error({
          'message': status === 404 ? 'The promo code you have entered is invalid or does not apply to this registration.' : data,
          'title': 'Invalid Code',
          'forceAction': true
        });
      });
    };

    $scope.deletePromotion = function (promoId) {
      if(permissions.permissionInt < permissionConstants.UPDATE){
        modalMessage.error(permissionRequiredMsg);
        return;
      }

      var regCopy = angular.copy($scope.registration);
      _.remove(regCopy.promotions, {id: promoId});
      $http.put('registrations/' + registration.id, regCopy).success(loadPayments).error(function () {
        modalMessage.error('An error occurred while deleting promotion.');
      });
    };

    $scope.filterUsedPromoCodes = function(p){
      var registrationPromoCodes = _.pluck($scope.registration.promotions, 'id');
      return !_.contains(registrationPromoCodes, p.id);
    };
  });
<commit_msg>remove negative CASH payment exception<commit_after>'use strict';

angular.module('confRegistrationWebApp')
  .controller('paymentModal', function ($scope, $modalInstance, modalMessage, $http, registration, conference, permissions, permissionConstants) {
    $scope.registration = registration;
    $scope.conference = conference;
    $scope.processing = false;
    $scope.activeTab = {};
    $scope.newTransaction = {
      registrationId: registration.id,
      amount: 0,
      sendEmailReceipt: false
    };
    var permissionRequiredMsg = 'You do not have permission to perform this action. Please contact an event administrator to request permission.';

    $scope.getRegistrantType = function(id) {
      return _.find(conference.registrantTypes, { 'id': id });
    };

    $scope.getBlock = function (blockId) {
      var allBlocks = [];
      _.each(conference.registrationPages, function (page) {
        allBlocks = allBlocks.concat(page.blocks);
      });
      return _.find(allBlocks, {id: blockId});
    };

    $scope.processTransaction = function () {
      if (_.isEmpty($scope.newTransaction.paymentType)) {
        modalMessage.error('Please select a transaction type.');
        return;
      }
      if (Number($scope.newTransaction.amount) <= 0) {
        $scope.newTransaction.errors.unshift('Transaction amount must be a positive number.');
      }

      if (!_.isEmpty($scope.newTransaction.errors)) {
        modalMessage.error({
          'title': 'Please correct the following errors:',
          'message': $scope.newTransaction.errors
        });
        return;
      }

      if(permissions.permissionInt < permissionConstants.UPDATE){
        if(permissions.permissionInt === permissionConstants.SCHOLARSHIP) {
          if($scope.newTransaction.paymentType !== 'SCHOLARSHIP'){
            modalMessage.error('Your permission level only allows scholarship payments to be added. Please contact an event administrator to request permission.');
            return;
          }
        }else{
          modalMessage.error(permissionRequiredMsg);
          return;
        }
      }

      if(permissions.permissionInt < permissionConstants.FULL && _.contains(['ADDITIONAL_EXPENSE', 'DISCOUNT'], $scope.newTransaction.paymentType)){
        modalMessage.error(permissionRequiredMsg);
        return;
      }

      var transaction = angular.copy($scope.newTransaction);
      delete transaction.errors;
      transaction.amount = Number(transaction.amount);
      var path = 'payments?sendEmailReceipt=' + transaction.sendEmailReceipt;
      delete transaction.sendEmailReceipt;

      if(_.contains(['ADDITIONAL_EXPENSE', 'DISCOUNT'], transaction.paymentType)) {
        if(!transaction.description){
          modalMessage.error('Please enter a description.');
          return;
        }

        path = 'expenses';
        if(transaction.paymentType === 'DISCOUNT'){
          transaction.amount = Number(transaction.amount) * -1;
        }
        delete transaction.paymentType;
      } else {
        transaction.readyToProcess = true;
      }

      if(transaction.paymentType === 'SCHOLARSHIP') {
        transaction.status = 'APPROVED';
      }

      $scope.processing = true;
      if(transaction.paymentType === 'CREDIT_CARD'){
        $http.get('payments/ccp-client-encryption-key', {cache: true}).success(function(ccpClientEncryptionKey) {
          ccp.initialize(ccpClientEncryptionKey);
          transaction.creditCard.lastFourDigits = ccp.getAbbreviatedNumber(transaction.creditCard.number);
          transaction.creditCard.number = ccp.encrypt(transaction.creditCard.number);
          transaction.creditCard.cvvNumber = ccp.encrypt(transaction.creditCard.cvvNumber);
          postTransaction(path, transaction);
        }).error(function() {
          $scope.processing = false;
          modalMessage.error('An error occurred while requesting the ccp encryption key.');
        });
      }else{
        delete transaction.creditCard;
        postTransaction(path, transaction);
      }
    };

    var postTransaction = function(path, transaction){
      $http.post(path, transaction).success(function () {
        loadPayments();
        if(path === 'expenses'){
          $scope.activeTab[2] = true;
        }else{
          $scope.activeTab[1] = true;
        }
      }).error(function (data) {
        modalMessage.error('Transaction failed. ' + data.errorMessage);
        $scope.processing = false;
      });
    };

    $scope.canBeRefunded = function (payment) {
      return payment.paymentType !== 'REFUND' &&
        payment.paymentType !== 'TRANSFER' &&
        payment.paymentType !== 'SCHOLARSHIP' &&
        $scope.calculateRefundableAmount(payment) > 0;
    };

    $scope.calculateRefundableAmount = function (payment) {
      if(angular.isUndefined(payment)) {
        return 0;
      }
      var sum = payment.amount;
      _.each($scope.registration.pastPayments, function (prevRefund) {
        if (prevRefund.paymentType === 'REFUND' && prevRefund.refundedPaymentId === payment.id) {
          sum -= prevRefund.amount;
        }
      });
      return sum;
    };

    $scope.startRefund = function (payment) {
      if(permissions.permissionInt < permissionConstants.UPDATE){
        modalMessage.error(permissionRequiredMsg);
        return;
      }
      $scope.paymentToRefund = payment;

      $scope.refund = {
        amount: $scope.calculateRefundableAmount(payment),
        refundedPaymentId: payment.id,
        registrationId: payment.registrationId,
        paymentType: 'REFUND',
        refundChannel: payment.paymentType,
        readyToProcess: true
      };
    };

    $scope.processRefund = function () {
      if(!$scope.refund.refundChannel){
        modalMessage.error('Please select a refund method.');
        return;
      }

      $scope.processing = true;
      $http.post('payments/', $scope.refund).success(function () {
        $scope.refund = null;
        loadPayments();
      }).error(function () {
        modalMessage.error('Refund failed.');
      }).finally(function(){
        $scope.processing = false;
      });
    };

    $scope.cancelRefund = function () {
      delete $scope.refund;
    };

    $scope.removeExpense = function (expense) {
      if(permissions.permissionInt < permissionConstants.UPDATE){
        modalMessage.error(permissionRequiredMsg);
        return;
      }

      modalMessage.confirm({
        'title': 'Delete Expense?',
        'question': 'Are you sure you want to delete this expense?'
      }).then(function(){
        $http.delete('expenses/' + expense.id).success(function () {
          loadPayments();
        }).error(function () {
          modalMessage.error('An error occurred while deleting this expense.');
        });
      });
    };

    $scope.savePaymentEdits = function (payment) {
      if(payment.paymentType === 'SCHOLARSHIP'){
        payment.status = 'APPROVED';
      }
      if(payment.paymentType === 'CHECK'){
        payment.status = 'RECEIVED';
      }

      $http.put('payments/' + payment.id, payment).success(function() {
        loadPayments();
        delete $scope.editPayment;
      }).error(function(){
        modalMessage.error('Payment could not be saved. Please verify all required fields are filled in correctly.');
      });
    };

    $scope.openEditPaymentRow = function (payment) {
      if(angular.isDefined($scope.editPayment) && $scope.editPayment.id === payment.id) {
        delete $scope.editPayment;
      } else {
        if(permissions.permissionInt >= permissionConstants.UPDATE || (permissions.permissionInt === permissionConstants.SCHOLARSHIP && payment.paymentType === 'SCHOLARSHIP')){
          $scope.editPayment = angular.copy(payment);
        }else{
          modalMessage.error(permissionRequiredMsg);
        }
      }
    };

    $scope.saveExpenseEdits = function (expense) {
      $http.put('expenses/' + expense.id, expense).success(function() {
        loadPayments();
        delete $scope.editExpense;
      });
    };

    var loadPayments = function() {
      $http.get('registrations/' + $scope.registration.id).success(function (data) {
        $scope.registration = data;
        $scope.processing = false;

        $scope.newTransaction = {
          registrationId: registration.id,
          amount: 0,
          sendEmailReceipt: false
        };
      });
    };

    $scope.deletePayment = function (payment) {
      if(permissions.permissionInt >= permissionConstants.UPDATE || (permissions.permissionInt === permissionConstants.SCHOLARSHIP && payment.paymentType === 'SCHOLARSHIP')){
      }else{
        modalMessage.error(permissionRequiredMsg);
        return;
      }
      modalMessage.confirm({
        'title':'Delete payment?',
        'question': 'Are you sure you want to delete this payment?'
      }).then(function(){
        $http.delete('payments/' + payment.id, payment).success(function () {
          loadPayments();
        }).error(function () {
          modalMessage.error('An error occurred while deleting this payment.');
        });
      });
    };

    $scope.openEditExpenseRow = function (expense) {
      if(angular.isDefined($scope.editExpense) && $scope.editExpense.id === expense.id) {
        delete $scope.editExpense;
      } else {
        if(permissions.permissionInt < permissionConstants.UPDATE){
          modalMessage.error(permissionRequiredMsg);
          return;
        }

        $scope.editExpense = angular.copy(expense);
      }
    };

    $scope.allowCreditCardPayments = function () {
      var allow = false;
      _.each($scope.registration.registrants, function(registrant) {
        var registrantType = $scope.getRegistrantType(registrant.registrantTypeId);
        if(registrantType.acceptCreditCards) {
          allow = true;
        }
      });

      return allow;
    };

    //auto set new transaction amount
    $scope.setTransactionAmount = function(){
      var paymentTypes = ['CREDIT_CARD', 'OFFLINE_CREDIT_CARD', 'SCHOLARSHIP', 'TRANSFER', 'CHECK', 'CASH'];

      if($scope.registration.remainingBalance > 0 && ($scope.newTransaction.amount === 0 || $scope.newTransaction.amount === '' || $scope.newTransaction.amount === '0') && _.contains(paymentTypes, $scope.newTransaction.paymentType)){
        $scope.newTransaction.amount = $scope.registration.remainingBalance;
      }
    };

    $scope.addPromotion = function(inputCode){
      $http.post('registrations/' + registration.id + '/promotions', {code: inputCode}).success(function () {
        loadPayments();
      }).error(function (data, status) {
        modalMessage.error({
          'message': status === 404 ? 'The promo code you have entered is invalid or does not apply to this registration.' : data,
          'title': 'Invalid Code',
          'forceAction': true
        });
      });
    };

    $scope.deletePromotion = function (promoId) {
      if(permissions.permissionInt < permissionConstants.UPDATE){
        modalMessage.error(permissionRequiredMsg);
        return;
      }

      var regCopy = angular.copy($scope.registration);
      _.remove(regCopy.promotions, {id: promoId});
      $http.put('registrations/' + registration.id, regCopy).success(loadPayments).error(function () {
        modalMessage.error('An error occurred while deleting promotion.');
      });
    };

    $scope.filterUsedPromoCodes = function(p){
      var registrationPromoCodes = _.pluck($scope.registration.promotions, 'id');
      return !_.contains(registrationPromoCodes, p.id);
    };
  });
<|endoftext|>


<commit_before>/***
TiddlySpace extensions for [[chrjs]]
***/
//{{{
(function($) {

tiddlyweb.routes.spaces = "{host}/spaces";
tiddlyweb.routes.space = "{host}/spaces/{name}";
tiddlyweb.routes.members = "{host}/spaces/{name}/members";
tiddlyweb.routes.member = "{host}/spaces/{name}/members/{username}";
tiddlyweb.routes.includes = "{host}/spaces/{name}";

tiddlyweb.Space = function(name, host) {
	tiddlyweb.Resource.apply(this, ["space", host]);
	this.name = name;
};
tiddlyweb.Space.prototype = new tiddlyweb.Resource();
$.extend(tiddlyweb.Space.prototype, {
	create: function(callback, errback) { // API wrapper
		this.put.apply(this, arguments);
	},
	members: function() {
		return new MemberCollection(this);
	},
	includes: function() {
		return new IncludesCollection(this);
	}
});

var Member = function(username, space) {
	tiddlyweb.Resource.apply(this, ["member", space.host]);
	this.name = space.name;
	this.username = username;
};
Member.prototype = new tiddlyweb.Resource();

var MemberCollection = function(space) {
	tiddlyweb.Collection.apply(this, ["members", space.host, {
		name: space.name
	}]);
};
MemberCollection.prototype = new tiddlyweb.Collection();
$.extend(MemberCollection.prototype, {
	add: function(username, callback, errback) {
		var member = new Member(username, this);
		member.put(callback, errback);
	},
	remove: function(username, callback, errback) {
		var member = new Member(username, this);
		member["delete"](callback, errback);
	}
});

var IncludesCollection = function(space) {
	tiddlyweb.Collection.apply(this, ["includes", space.host, {
		name: space.name
	}]);
	this.inclusions = [];
};
IncludesCollection.prototype = new tiddlyweb.Collection();
$.extend(IncludesCollection.prototype, {
	get: function(callback, errback) {
		var self = this;
		var recipe = new tiddlyweb.Recipe(this.name + "_public", this.host);
		recipe.get(function(recipe, status, xhr) {
			var inclusions = $.map(recipe.recipe, function(item, i) { // TODO: refactor to canonicalize; move to TiddlySpaceConfig!?
				var arr = item[0].split("_public");
				return (arr[0] != self.name && arr[1] === "") ? arr[0] : null;
			});
			self.inclusions = inclusions;
			callback(inclusions, status, xhr);
		}, function(xhr, error, exc) {
			errback(xhr, error, exc, self);
		});
	},
	add: function(name, callback, errback) {
		var self = this;
		var names = typeof(name) === "string" ? [ name ] : name;
		$.ajax({
			type: "post",
			url: this.route(),
			contentType: "json",
			data: $.toJSON({ "subscriptions": names }),
			success: function(response, status, xhr) {
				callback(self, status, xhr);
			},
			errback: function(xhr, error, exc) {
				errback(xhr, error, exc, self);
			}
		});
	},
	remove: function(name, callback, errback) {
		var self = this;
		var names = typeof(name) === "string" ? [ name ] : name;
		$.ajax({
			type: "post",
			contentType: "json",
			url: this.route(),
			data: $.toJSON({ "unsubscription": names }),
			success: function(response, status, xhr) {
				callback(self, status, xhr);
			},
			errback: function(xhr, error, exc) {
				errback(xhr, error, exc, self);
			}
		});
	}
});
})(jQuery);
//}}}
<commit_msg>undo repetition of routes<commit_after>/***
TiddlySpace extensions for [[chrjs]]
***/
//{{{
(function($) {

tiddlyweb.routes.spaces = "{host}/spaces";
tiddlyweb.routes.space = "{host}/spaces/{name}";
tiddlyweb.routes.members = "{host}/spaces/{name}/members";
tiddlyweb.routes.member = "{host}/spaces/{name}/members/{username}";

tiddlyweb.Space = function(name, host) {
	tiddlyweb.Resource.apply(this, ["space", host]);
	this.name = name;
};
tiddlyweb.Space.prototype = new tiddlyweb.Resource();
$.extend(tiddlyweb.Space.prototype, {
	create: function(callback, errback) { // API wrapper
		this.put.apply(this, arguments);
	},
	members: function() {
		return new MemberCollection(this);
	},
	includes: function() {
		return new IncludesCollection(this);
	}
});

var Member = function(username, space) {
	tiddlyweb.Resource.apply(this, ["member", space.host]);
	this.name = space.name;
	this.username = username;
};
Member.prototype = new tiddlyweb.Resource();

var MemberCollection = function(space) {
	tiddlyweb.Collection.apply(this, ["members", space.host, {
		name: space.name
	}]);
};
MemberCollection.prototype = new tiddlyweb.Collection();
$.extend(MemberCollection.prototype, {
	add: function(username, callback, errback) {
		var member = new Member(username, this);
		member.put(callback, errback);
	},
	remove: function(username, callback, errback) {
		var member = new Member(username, this);
		member["delete"](callback, errback);
	}
});

var IncludesCollection = function(space) {
	tiddlyweb.Collection.apply(this, ["space", space.host, {
		name: space.name
	}]);
	this.inclusions = [];
};
IncludesCollection.prototype = new tiddlyweb.Collection();
$.extend(IncludesCollection.prototype, {
	get: function(callback, errback) {
		var self = this;
		var recipe = new tiddlyweb.Recipe(this.name + "_public", this.host);
		recipe.get(function(recipe, status, xhr) {
			var inclusions = $.map(recipe.recipe, function(item, i) { // TODO: refactor to canonicalize; move to TiddlySpaceConfig!?
				var arr = item[0].split("_public");
				return (arr[0] != self.name && arr[1] === "") ? arr[0] : null;
			});
			self.inclusions = inclusions;
			callback(inclusions, status, xhr);
		}, function(xhr, error, exc) {
			errback(xhr, error, exc, self);
		});
	},
	add: function(name, callback, errback) {
		var self = this;
		var names = typeof(name) === "string" ? [ name ] : name;
		$.ajax({
			type: "post",
			url: this.route(),
			contentType: "json",
			data: $.toJSON({ "subscriptions": names }),
			success: function(response, status, xhr) {
				callback(self, status, xhr);
			},
			errback: function(xhr, error, exc) {
				errback(xhr, error, exc, self);
			}
		});
	},
	remove: function(name, callback, errback) {
		var self = this;
		var names = typeof(name) === "string" ? [ name ] : name;
		$.ajax({
			type: "post",
			contentType: "json",
			url: this.route(),
			data: $.toJSON({ "unsubscription": names }),
			success: function(response, status, xhr) {
				callback(self, status, xhr);
			},
			errback: function(xhr, error, exc) {
				errback(xhr, error, exc, self);
			}
		});
	}
});
})(jQuery);
//}}}
<|endoftext|>


<commit_before>'use strict';

angular.module('hackApp')
  .directive('newsComponent', function ($timeout, Talk) {
    return {
      templateUrl: 'partials/newscomponent',
      restrict: 'AE',
      scope: {
    	  title: "=ntitle",
    	  summary: "=",
    	  fulltext: "=full",
    	  image: "="
      },
      link: function(scope, element, attrs) {
        scope.expanded = false;
        scope.full = false;

        scope.fulltext = '
' + scope.fulltext.split('\n').join('

') + '
';

        scope.talk = function(){
          Talk.queue(scope.title);
          if(scope.full){
            Talk.queue(scope.fulltext);
          }else{
            _.each(scope.summary, function(sum){
              Talk.queue(sum);
            });
          }
        };

    	  scope.expand = function() {
    		  scope.expanded = !scope.expanded;
    	  };

        scope.gofull = function() {
          scope.full = !scope.full;
        };
      }
    };
  });
<commit_msg>Test.<commit_after>'use strict';

angular.module('hackApp')
  .directive('newsComponent', function ($timeout, Talk) {
    return {
      templateUrl: 'partials/newscomponent',
      restrict: 'AE',
      scope: {
    	  title: "=ntitle",
    	  summary: "=",
    	  fulltext: "=full",
    	  image: "="
      },
      link: function(scope, element, attrs) {
        scope.expanded = false;
        scope.full = false;

        scope.fulltextNoP = angular.copy(scope.fulltext);

        scope.fulltext = '
' + scope.fulltext.split('\n').join('

') + '
';

        scope.talk = function(){
          Talk.queue(scope.title);
          if(scope.full){
            Talk.queue(scope.fulltextNoP);
          }else{
            _.each(scope.summary, function(sum){
              Talk.queue(sum);
            });
          }
        };

    	  scope.expand = function() {
    		  scope.expanded = !scope.expanded;
    	  };

        scope.gofull = function() {
          scope.full = !scope.full;
        };
      }
    };
  });
<|endoftext|>