Compare commits

..

257 Commits

Author SHA1 Message Date
unknown 10b8212980 update rows per page
continuous-integration/drone/push Build is passing Details
2025-01-25 09:36:25 -07:00
unknown 0d13dcdb6d update tax year in another spot
continuous-integration/drone/push Build is passing Details
2025-01-25 09:08:58 -07:00
unknown 6f45815e64 force update
continuous-integration/drone/push Build is passing Details
2025-01-25 09:03:20 -07:00
unknown 9d5b6e5935 update tax year to 2024 2025-01-25 09:02:25 -07:00
unknown 01f8404805 force build
continuous-integration/drone/push Build is passing Details
2024-12-06 07:32:17 -07:00
unknown 51579d2aee Add kfgl public information file
continuous-integration/drone/push Build was killed Details
2024-12-06 07:21:05 -07:00
unknown dfeb90828f update email creds
continuous-integration/drone/push Build is passing Details
2024-10-28 22:17:41 -06:00
unknown 8fd3c784e8 update pastor bio
continuous-integration/drone/push Build is passing Details
2024-10-06 16:05:26 -06:00
unknown 13d5eca9ef update home logic
continuous-integration/drone/push Build is passing Details
2024-08-04 15:06:49 -06:00
unknown 70146f0e73 force build
continuous-integration/drone/push Build is failing Details
2024-08-04 15:02:54 -06:00
unknown ca31b97b66 force build
continuous-integration/drone/push Build is failing Details
2024-08-04 14:57:48 -06:00
unknown ad23f0cfc7 Victory Gospel Crusade 2024-08-04 14:56:51 -06:00
unknown d5369eaaad Remove undefined for not entered phone
continuous-integration/drone/push Build is passing Details
2024-03-28 21:32:43 -06:00
unknown 14777e9171 add br after location in body
continuous-integration/drone/push Build is passing Details
2024-03-28 21:27:06 -06:00
unknown c851c8a070 Fix body undefined
continuous-integration/drone/push Build is passing Details
2024-03-28 21:20:48 -06:00
unknown 9c98cb51ba Fix issue with non required email
continuous-integration/drone/push Build is passing Details
2024-03-28 21:16:39 -06:00
unknown e66a014572 Don't require email for balloon
continuous-integration/drone/push Build is passing Details
2024-03-28 21:09:42 -06:00
unknown ad9ec5e7c6 Don't require message
continuous-integration/drone/push Build is passing Details
2024-03-28 21:01:59 -06:00
unknown 96d7270332 balloon contact
continuous-integration/drone/push Build is passing Details
2024-03-28 20:54:05 -06:00
unknown 58bd3c28b0 Force build
continuous-integration/drone/push Build is passing Details
2024-03-28 20:24:52 -06:00
unknown 78e80c4b79 add balloon component 2024-03-28 20:23:23 -06:00
unknown 53d9bd9cff update photo size
continuous-integration/drone/push Build is passing Details
2024-02-13 20:24:19 -07:00
unknown e8911b96f7 update photo size
continuous-integration/drone/push Build is passing Details
2024-02-13 20:17:36 -07:00
unknown 78e36d8446 force build
continuous-integration/drone/push Build is passing Details
2024-02-13 20:12:13 -07:00
unknown e37304533e Update family info 2024-02-13 20:11:13 -07:00
unknown d7d41931ee adjust rows per page
continuous-integration/drone/push Build is passing Details
2024-01-06 16:54:33 -07:00
unknown 2e0ec2b51d adjust rows per page
continuous-integration/drone/push Build is passing Details
2024-01-06 16:47:11 -07:00
unknown a1e32936df adjust rows per page
continuous-integration/drone/push Build is passing Details
2024-01-06 16:37:43 -07:00
unknown add85ebda7 reduce rows for first page
continuous-integration/drone/push Build is passing Details
2024-01-06 16:32:09 -07:00
unknown d898b2abae adjust space size
continuous-integration/drone/push Build is passing Details
2024-01-06 15:31:41 -07:00
unknown b0002c2107 increase max old space size
continuous-integration/drone/push Build was killed Details
2024-01-06 15:14:05 -07:00
unknown b46bc5221e adjust max old space size
continuous-integration/drone/push Build was killed Details
2024-01-06 15:09:06 -07:00
unknown f302b69c9a remove whitespace
continuous-integration/drone/push Build was killed Details
2024-01-06 14:59:31 -07:00
unknown da33858642 update tax year to 2023
continuous-integration/drone/push Build was killed Details
2024-01-06 14:25:31 -07:00
Dan 98ca74cadf remove covid and fcc from home page
continuous-integration/drone Build is passing Details
2023-08-01 15:58:02 -06:00
Dan 8fd40c8dc6 Allow adding a user from UI
continuous-integration/drone Build is passing Details
2023-08-01 15:48:14 -06:00
Dan 06c49558c4 Update API Url
continuous-integration/drone Build is passing Details
continuous-integration/drone/promote/production Build is passing Details
2023-08-01 11:47:23 -06:00
djabsher 0f88898a67 update url for sermons
continuous-integration/drone Build is passing Details
2023-07-30 04:05:55 +00:00
djabsher b8d4301429 Update router for static media files
continuous-integration/drone Build is passing Details
2023-07-30 03:58:21 +00:00
djabsher 900029783f change sql host
continuous-integration/drone Build is passing Details
2023-07-29 17:42:31 +00:00
djabsher 2603f5208a connect to mysql docker network 2023-07-29 17:41:17 +00:00
djabsher c420043ba3 Update drone.yml
continuous-integration/drone Build is passing Details
2023-07-29 05:41:27 +00:00
djabsher 4373e27d40 Add verbose to ng build
continuous-integration/drone Build is passing Details
2023-07-29 05:07:36 +00:00
djabsher d0eaddf1d9 Add verbose to ng build
continuous-integration/drone Build is failing Details
2023-07-29 05:05:19 +00:00
Me 89b21ddbd8 raise max old space size to 1000
continuous-integration/drone Build is failing Details
2023-07-28 18:24:08 -06:00
Me 61cff5f973 lower max old space to 500
continuous-integration/drone Build is failing Details
2023-07-28 18:22:09 -06:00
Me 3d3c0b9669 lower size again to 750
continuous-integration/drone Build is failing Details
2023-07-28 17:22:02 -06:00
Me d6178b2ca4 lower max old space size
continuous-integration/drone Build was killed Details
2023-07-28 17:14:54 -06:00
Me c8879f964d Adjust max old space size when building angular
continuous-integration/drone Build is failing Details
2023-07-28 17:03:23 -06:00
Me a038c449e7 update media directory
continuous-integration/drone Build is failing Details
2023-07-28 15:47:57 -06:00
unknown 6701cf2faa order by transaction date 2023-01-07 23:28:47 -07:00
unknown 864b838b69 Adjust rows per page 2023-01-07 20:58:36 -07:00
unknown 3858ef2299 Adjust rows per page 2023-01-07 20:56:01 -07:00
unknown cf0a57e8fd undo depth 2023-01-07 20:20:36 -07:00
unknown 38a1cdd486 adjust clone depth 2023-01-07 20:18:17 -07:00
unknown d7661b4649 update tax year on individual report to 2022 2023-01-05 19:07:25 -07:00
unknown b319f3c109 force build for tax year change 2023-01-05 19:00:31 -07:00
unknown 01766dc47f change to 2022 2023-01-05 18:59:03 -07:00
unknown afa4fe57ba force build 2022-09-05 14:56:28 -06:00
unknown 25a659d2cf updates for cowboy carnival and pastor picture 2022-09-05 14:54:55 -06:00
unknown 06b01f250e update pastor image 2022-07-07 19:49:08 -06:00
unknown 6918ce605b update pastor image 2022-07-07 19:46:31 -06:00
unknown d8f295f1ba update tax year again 2022-01-15 21:21:26 -07:00
unknown bb341a5659 build 2022-01-15 21:13:44 -07:00
unknown 6c3652f6ca update tax year to 2021 2022-01-15 21:10:30 -07:00
dan c97268133d Break long url 2021-11-12 22:36:52 -07:00
dan 2f63d43c47 Add FM Radio Station Permit Announcement 2021-11-12 22:03:42 -07:00
dan 2cd2685f91 remove covid guidelines 2021-01-28 00:07:41 -07:00
dan 6f329251cc Format date in UTC for contributor report 2021-01-03 13:35:48 -07:00
dan a51644bf9f update tax year 2020-12-29 15:47:27 -07:00
dan e4f976ea2a update tax years 2020-12-29 15:22:37 -07:00
dan 73bfb61f9b Tax year update 2020-12-29 15:07:48 -07:00
dan f9e42fa69f update tax year 2020-12-29 15:03:06 -07:00
dan 396f0cb7a1 build 2020-10-29 18:47:21 -06:00
dan d82152684f build 2020-10-29 18:45:04 -06:00
dan 7d240c8fc9 Add dockerignore 2020-10-29 18:41:06 -06:00
dan a344233780 update static url to ofbbutte 2020-10-28 21:37:28 -06:00
dan 35c751e35f update sermon locations 2020-10-28 00:28:35 -06:00
dan 48aa37354f sermon location 2020-10-28 00:14:00 -06:00
dan 82886951f2 bind mount 2020-10-28 00:11:00 -06:00
dan f159d04987 update emailer 2020-10-27 21:07:14 -06:00
dan 63b00fcd8a update mail transporter 2020-10-27 21:03:23 -06:00
dan 51abacb9f2 log error 2020-10-27 20:08:10 -06:00
dan 75415de5c7 update emailer 2020-10-27 20:00:12 -06:00
dan 2797c504b4 download mp3 2020-10-27 19:46:43 -06:00
dan 05fc3a220a Update download location 2020-10-27 15:29:15 -06:00
dan 6fb5e5b27c udate static media url 2020-10-27 12:29:04 -06:00
dan b05d63ebef update host 2020-10-27 01:14:08 -06:00
dan 35f8a69c96 update connection 2020-10-27 00:50:35 -06:00
dan f144faa870 update connection 2020-10-27 00:37:09 -06:00
dan f3fbb80e19 test 2020-10-27 00:21:48 -06:00
dan 069c2d4f83 Always restart 2020-10-27 00:20:46 -06:00
dan d3e9d764e6 restart 2020-10-27 00:19:36 -06:00
dan 0668feef04 update container name 2020-10-27 00:18:02 -06:00
dan 5b270b76a7 remove old container 2020-10-27 00:17:23 -06:00
dan d1c8e2d344 update title 2020-10-27 00:16:03 -06:00
dan 867991eb68 test 2020-10-27 00:14:56 -06:00
dan f13dc3a191 test 2020-10-27 00:14:02 -06:00
dan 34675f9ae1 test 2020-10-27 00:09:13 -06:00
dan 18ecf3337d stop previous container 2020-10-27 00:08:07 -06:00
dan 7a67e6eea9 pipeline update 2020-10-27 00:05:46 -06:00
dan cd0bf8f317 fix pipeline 2020-10-27 00:04:12 -06:00
dan d91d4e5fa7 fix docker command 2020-10-27 00:03:31 -06:00
dan 0b267fd68f test 2020-10-26 23:59:12 -06:00
dan 0683120c9f test 2020-10-26 23:42:35 -06:00
dan 2b6f92dabb test 2020-10-26 23:40:56 -06:00
dan bf81a30d8b Test 2020-10-26 23:39:27 -06:00
dan 9c59ec76f0 arrange 2020-10-26 23:34:24 -06:00
dan 0631ad0f04 Remove client folder 2020-10-26 23:32:22 -06:00
dan 5a88277d46 update server package.json file 2020-10-26 23:29:06 -06:00
dan 2de90ed6d3 fix more directories 2020-10-26 23:20:51 -06:00
dan f70bed9ebf fix directory 2020-10-26 23:20:13 -06:00
dan ecbbd7d2a5 fix drone 2020-10-26 23:19:17 -06:00
dan aadd145eef dockerfile 2020-10-26 23:18:24 -06:00
dan 8789391648 test docker-compose 2020-10-26 22:16:44 -06:00
dan 8086288cda modify docker 2020-10-26 22:08:32 -06:00
dan bcc551e405 update dockerfile 2020-10-26 21:41:40 -06:00
dan c285b9cb95 docker_sock host 2020-10-26 21:22:35 -06:00
dan cd659e6249 mount docker sock 2020-10-26 21:21:11 -06:00
dan 336152630b apt update 2020-10-26 21:02:55 -06:00
dan 47d94542b6 sudo dockerd 2020-10-26 21:02:21 -06:00
dan 96ad62751d build docker 2020-10-26 21:01:24 -06:00
dan f031ea3c9b update dockerfile 2020-10-26 20:58:31 -06:00
dan baa8451918 docker build 2020-10-26 20:55:30 -06:00
dan 9777ecb0f0 docker build 2020-10-26 20:54:31 -06:00
dan f89f673cdc Add dockerfile 2020-10-26 20:53:27 -06:00
dan b758d2686e Docker test 2020-10-26 20:46:38 -06:00
dan 22391f264e build client and server 2020-10-26 20:31:54 -06:00
dan f04b5c7895 deploy step 2020-10-26 18:01:01 -06:00
dan c629d2fc2a cd to server/www 2020-10-26 17:35:43 -06:00
dan c2a4c5f63e Try again 2020-10-26 17:23:27 -06:00
dan cafa43e2e7 quote www 2020-10-26 17:10:56 -06:00
dan cb1f61257e Add backtick 2020-10-26 17:02:36 -06:00
dan 00943dc324 ignore node 2020-10-26 16:35:06 -06:00
dan 054c172f17 Remove quotes from commands 2020-10-26 15:54:55 -06:00
dan d7053b0325 it works 2020-10-26 15:41:20 -06:00
dan 2c1f48f189 apt update 2020-10-26 15:40:16 -06:00
dan 6b11cdd0ce Add tree to start 2020-10-26 15:38:37 -06:00
dan 10561f523e sudo install tree 2020-10-26 15:31:37 -06:00
dan f4653c7098 install tree 2020-10-26 15:13:21 -06:00
dan d91176713e Fix syntax error 2020-10-26 12:24:02 -06:00
dan b57cec7136 Fix syntax error 2020-10-26 12:23:37 -06:00
dan 8b363f4cd6 Tree command 2020-10-26 12:22:25 -06:00
dan ef34bdd52a Add configuration 2020-10-26 12:06:35 -06:00
dan d9c91881ef test www 2020-10-26 11:35:36 -06:00
dan 5f4550998c Test server/src 2020-10-26 11:17:37 -06:00
dan b7c98ee522 test ls 2020-10-26 11:16:02 -06:00
dan cdd91b047c Reverse slashes 2020-10-25 22:11:57 -06:00
dan 3563df5791 Fix ng build for drone 2020-10-25 22:11:17 -06:00
dan 113979f09b cd to client folder 2020-10-25 22:06:27 -06:00
dan 4bb0e1f39a Add npm install 2020-10-25 22:04:43 -06:00
dan 9f9d0fd384 Add drone.yml 2020-10-25 22:03:42 -06:00
dan 9e7f02e2be Merge remote-tracking branch 'origin/test' 2020-05-18 00:33:04 -06:00
dan 6204f855f3 update videoservices.json 2020-05-18 00:31:29 -06:00
dan d803501c31 Add Youtube Api 2020-05-18 00:29:26 -06:00
dan 7dd6c656f2 Force build 2020-05-11 20:32:47 -06:00
dan e2ff93c722 Updated reopening guidelines 2020-05-11 20:27:29 -06:00
dan dd3934ff9c force update 2020-04-27 22:29:27 -06:00
dan b056f0316d Update videServices.json 2020-04-27 22:26:50 -06:00
dan 93f6503bac Change mp3 mime type to audio/mpeg 2020-04-27 22:23:54 -06:00
dan b3a73dd47b change video link 2020-04-26 12:26:06 -06:00
dan ef5d4d8ee2 Reopening Guidelines 2020-04-23 18:29:32 -06:00
dan c1b87f270a Force build 2020-03-30 23:04:08 -06:00
dan af4b336327 Fix typo 2020-03-30 22:59:45 -06:00
dan e6781aed41 update image url 2020-03-30 20:10:38 -06:00
dan 2e00913ac9 Fix bugs 2020-03-30 20:03:37 -06:00
dan 08312e4e59 Update sharing api 2020-03-30 19:58:36 -06:00
dan 8a58b35d13 Update meta for shares 2020-03-30 19:54:54 -06:00
dan abf20f07de update router data 2020-03-30 19:29:04 -06:00
dan 2427623f26 update sharing 2020-03-30 19:27:19 -06:00
dan ea85959a14 Other sharing changes to pull from json 2020-03-30 19:10:41 -06:00
dan 8b3fafd7f0 force rebuild 2020-03-30 13:56:15 -06:00
dan e9d366612f Update json 2020-03-30 13:54:50 -06:00
dan 2585aa8625 Merge remote-tracking branch 'origin/test' 2020-03-30 13:54:28 -06:00
dan cadf1b1441 update url in share 2020-03-30 13:53:03 -06:00
dan e142786581 Add second covid tract 2020-03-30 13:52:36 -06:00
dan c834ff4a75 Fix req.hostname 2020-03-29 21:39:41 -06:00
dan 3970ccd93f remove url from data 2020-03-29 21:36:19 -06:00
dan 6394385986 Add colon 2020-03-29 21:16:12 -06:00
dan 2a20f77c9e add all data to url 2020-03-29 21:13:30 -06:00
dan 93f3f02e73 Add URL back to og 2020-03-29 21:08:25 -06:00
dan be06ad5c77 Remove url from share 2020-03-29 19:27:07 -06:00
dan eadbfe7fcc og url 2020-03-29 19:22:24 -06:00
dan d257f897b6 force update 2020-03-29 17:25:05 -06:00
dan e9a54ec5f5 Update json 2020-03-29 17:21:25 -06:00
dan 2a1bc133d2 Adjust share url 2020-03-29 17:15:57 -06:00
dan 1acf91ae04 Dont add url to fb share 2020-03-29 17:11:39 -06:00
dan ca663261be Adjust encoding 2020-03-29 16:54:18 -06:00
dan 875ae030f7 Enhance fb sharing 2020-03-29 16:03:18 -06:00
dan c2d927788a update other share option 2020-03-29 15:36:52 -06:00
dan d4598d76c0 Add other share option 2020-03-29 15:04:42 -06:00
dan 1e422fa9d8 Add youtube embeds 2020-03-29 13:12:51 -06:00
dan cd64706b30 Add share for tract 2020-03-29 12:18:50 -06:00
dan 39c6e1fd98 Add COVID Tract 2020-03-28 20:47:18 -06:00
dan 6f26bb859d Update live page 2020-03-23 20:10:59 -06:00
dan a2edf88fb1 update countdown and archived 2020-03-22 20:41:15 -06:00
dan ba8903c3ac update isready 2020-03-22 17:18:31 -06:00
dan 52caeed90f Update countdown and services 2020-03-22 17:04:28 -06:00
dan c8ff77211d Change video source 2020-03-22 12:41:26 -06:00
dan 4221f3f7db force rebuild 2020-03-22 10:29:26 -06:00
dan e1d057bfac remove test 2020-03-22 10:26:17 -06:00
dan b9ee133acb Add vid checker 2020-03-22 10:25:06 -06:00
dan fdac58f4dc Force update 2020-03-21 22:15:00 -06:00
dan b4e5960462 Force rebuild 2020-03-21 22:11:24 -06:00
dan e5011512ee Force update 2020-03-21 22:07:00 -06:00
dan fbafd051a8 Force update 2020-03-21 22:05:22 -06:00
dan 27f33320e6 Add video refresh 2020-03-21 21:57:38 -06:00
dan aeb019098c increase live font size 2020-03-21 03:47:00 -06:00
dan 94e2e7cf61 Adjust live stream font 2020-03-21 03:43:26 -06:00
dan 04fd77c1be Add live video countdown 2020-03-21 03:29:57 -06:00
dan a5dec99e4b force build 2020-01-04 22:53:41 -07:00
dan 4900c26d51 right align amount 2020-01-04 22:49:56 -07:00
dan 2243d00a18 Update title 2020-01-04 22:36:55 -07:00
dan 0af9b52d89 Force build 2019-12-31 16:42:59 -07:00
dan 9bd37dc600 Change taxyear to year 2019-12-31 16:39:29 -07:00
dan 590d17777d Update verbiage on report 2019-12-31 16:37:00 -07:00
dan aae13b1de5 Update wording 2019-12-31 16:21:09 -07:00
dan 10115646e9 Exclude goodsorservices; adjust contribution report 2019-12-31 16:15:39 -07:00
dan 95e90279ee Type 1 is cash, 2 is check 2019-12-31 12:23:49 -07:00
dan 50923319fb Fix fund and type 2019-12-31 11:54:07 -07:00
dan 73008adc66 add page numbers 2019-12-31 03:37:58 -07:00
dan 0c26eda95c Add chart.js @types dependency 2019-12-31 01:09:24 -07:00
dan 27f9a5f3f3 Update giving reports 2019-12-31 00:58:06 -07:00
dan 4b5a889efc format date 2019-11-25 20:41:40 -07:00
dan 99f8c96590 pull transactions from db 2019-11-24 14:40:19 -07:00
dan 2782a729f0 Multiple reports 2019-11-24 02:35:47 -07:00
dan 9611b90b71 add hilight and address 2019-11-24 01:27:15 -07:00
dan f2dd59725a table width 100 2019-11-24 00:04:36 -07:00
dan e528941b65 Add contributor print report 2019-11-23 23:20:10 -07:00
dan 1408a7f11e populate goods or services 2019-11-23 12:01:04 -07:00
dan 3be4ad6617 Fix errors 2019-11-23 11:56:21 -07:00
dan ba9ab39a40 import matcheckboxmodule 2019-11-23 11:46:31 -07:00
dan 0bdad0f3da import matcheckbox 2019-11-23 11:44:27 -07:00
dan e57da0ddd5 add goods or services 2019-11-23 11:41:30 -07:00
dan d2a88e1cd0 Get tax year from transaction date 2019-11-23 11:26:13 -07:00
dan 5a33068e2b Flip contributors match 2019-11-23 11:10:53 -07:00
dan a471b3121b add red border when dates do not match 2019-11-23 11:08:15 -07:00
dan b886d79879 Add transaction date 2019-11-23 10:51:20 -07:00
dan ff8880dd2a Auto hide VBS 2019-08-01 22:18:20 -06:00
dan b366733a52 add margin to VBS bottom 2019-08-01 22:12:13 -06:00
dan 37155b3563 Add VBS Image 2019-08-01 22:04:59 -06:00
dan e271d55fa7 add link to contact page for missionary form 2019-07-25 20:27:57 -06:00
dan 0e18651ff6 Modify missionary form 2019-07-24 23:13:33 -06:00
dan d25377cdbe Update environments 2019-07-24 22:33:06 -06:00
dan bda0b385ae Update fellowship, error popup, and submit label 2019-07-22 22:49:06 -06:00
dan c7240f9167 Update missionary form api 2019-07-22 22:08:53 -06:00
dan ee899f9f90 another typo 2019-07-22 01:03:28 -06:00
dan 59c4f9f89e Fix recommendation typo 2019-07-22 00:59:58 -06:00
dan 70817ad3a0 All Fields and service for missionary form 2019-07-22 00:44:35 -06:00
dan a27d5890a8 Adjust transaction popuup 2019-07-07 16:34:44 -06:00
dan 67d9ce7802 SaveAndAdd Button 2019-07-07 00:56:13 -06:00
dan 46b4e1dcd0 update transactions layout 2019-07-07 00:36:49 -06:00
dan 219120fe71 Update transaction styling 2019-07-06 00:49:13 -06:00
dan 7bad3c6045 update cli 2019-07-05 22:21:56 -06:00
dan 1b6e16e9fe force build 2019-07-05 22:11:18 -06:00
dan e66a2114c5 Force build 2019-07-05 22:04:06 -06:00
dan b78cf09e23 Fix build error 2019-07-05 22:01:01 -06:00
129 changed files with 17464 additions and 554 deletions

3
.dockerignore 100644
View File

@ -0,0 +1,3 @@
node_modules
.git
.gitignore

View File

@ -29,6 +29,14 @@
"scripts": []
},
"configurations": {
"test": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.test.ts"
}
]
},
"production": {
"fileReplacements": [
{

13512
Client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,20 +21,24 @@
"@angular/platform-browser": "~7.2.0",
"@angular/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0",
"chart.js": "^2.9.3",
"chartjs-plugin-datalabels": "^0.7.0",
"core-js": "^2.5.4",
"hammerjs": "^2.0.8",
"luxon": "^1.22.0",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.12.0",
"@angular/cli": "~7.2.2",
"@angular/cli": "^7.3.8",
"@angular/compiler-cli": "~7.2.0",
"@angular/language-service": "~7.2.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"@types/chart.js": "^2.9.7",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",

View File

@ -13,6 +13,15 @@ import { SalvationPageComponent } from './components/salvation-page/salvation-pa
import { CampPageComponent } from './components/camp-page/camp-page.component';
import { MembersPageComponent } from './components/members-page/members-page.component';
import { AddTransactionPageComponent } from './components/add-transaction-page/add-transaction-page.component';
import { MissionaryFormPageComponent } from './components/missionary-form-page/missionary-form-page.component';
import { ContributorYearlyReportComponent } from './components/contributor-yearly-report/contributor-yearly-report.component';
import { ContributorAllReportsComponent } from './components/contributor-all-reports/contributor-all-reports.component';
import { LiveStreamComponent } from './components/live-stream/live-stream.component';
import { VideoServicesComponent } from './components/video-services/video-services.component';
import { ImageComponent } from './components/image/image.component';
import { VideoComponent } from './components/video/video.component';
import { FCCPermitPageComponent } from './components/fcc-permit-page/fcc-permit-page.component';
import { BalloonComponent } from './components/balloon/balloon.component';
const routes =
[
@ -36,10 +45,18 @@ const routes =
path: 'contact',
component: ContactPageComponent
},
{
path: 'missionary',
component: MissionaryFormPageComponent
},
{
path: 'sermons',
component: SermonsComponent
},
{
path: 'video',
component: VideoComponent
},
{
path: 'sermons/:id',
component: SermonsComponent
@ -64,13 +81,45 @@ const routes =
path: 'user',
component: MembersPageComponent
},
{
path: 'covid',
component: ImageComponent,
data: {
imageUrl: 'assets/images/tiny/covid_tract.jpg',
shareName: 'covid'
}
},
{
path: 'covid2',
component: ImageComponent,
data: {
imageUrl: 'assets/images/tiny/covid_tract_2.jpg',
shareName: 'covid2'
}
},
{
path: 'camp',
component: CampPageComponent
},
{
path: 'live',
component: LiveStreamComponent
},
{
path: 'transactions/add',
component: AddTransactionPageComponent
},
{
path: 'contributor/report',
component: ContributorAllReportsComponent
},
{
path: 'fmpermit',
component: FCCPermitPageComponent
},
{
path: 'balloon',
component: BalloonComponent
}
]

View File

@ -3,6 +3,7 @@ import { GoogleAnalyticsService } from './services/google-analytics.service';
import { WindowRefService } from './services/window-ref.service';
import { EmailService } from './services/email.service';
import { SermonService } from './services/sermon.service';
import { PrintService } from './services/print-service';
import { LoginService } from './services/login.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
@ -15,7 +16,9 @@ import { MatButtonModule,
MatDialogModule,
MatSelectModule,
MatOptionModule,
MatAutocompleteModule} from '@angular/material';
MatRadioModule,
MatAutocompleteModule,
MatCheckboxModule} from '@angular/material';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import 'hammerjs';
@ -50,6 +53,8 @@ import { EventLargeComponent } from './components/event-large/event-large.compon
import { SalvationPageComponent } from './components/salvation-page/salvation-page.component';
import { MediaPageComponent } from './components/media-page/media-page.component';
import { VideoPopupComponent } from './components/popups/video-popup/video-popup.component';
import { FCCPermitPageComponent } from './components/fcc-permit-page/fcc-permit-page.component';
import { BalloonComponent } from './components/balloon/balloon.component';
//Directives
import { FadeInOnScrollDirective } from './directives/fade-in-on-scroll.directive';
@ -68,6 +73,20 @@ import { AddUserPopupComponent } from './components/popups/add-user-popup/add-us
import { UserService } from './services/user.service';
import { TransactionService } from './services/transaction.service';
import { AddTransactionPageComponent } from './components/add-transaction-page/add-transaction-page.component';
import { AddTransactionPopupComponent } from './components/add-transaction-page/add-transaction-popup/add-transaction-popup.component';
import { MissionaryFormPageComponent } from './components/missionary-form-page/missionary-form-page.component';
import { MissionarySupportService } from './services/missionary-support-service';
import { ContributorYearlyReportComponent } from './components/contributor-yearly-report/contributor-yearly-report.component';
import { ContributorAllReportsComponent } from './components/contributor-all-reports/contributor-all-reports.component';
import { LiveStreamComponent } from './components/live-stream/live-stream.component';
import { VideoServicesComponent } from './components/video-services/video-services.component';
import { ImageComponent } from './components/image/image.component';
import { YoutubeListComponent } from './components/youtube-list/youtube-list.component';
import { VideoComponent } from './components/video/video.component';
import { YoutubeListService } from './components/youtube-list/youtube-list-service';
import { VideoItemComponent } from './components/video-item/video-item.component';
import { YoutubePopupComponent } from './components/popups/youtube-popup/youtube-popup.component';
@ -92,6 +111,7 @@ import { AddTransactionPageComponent } from './components/add-transaction-page/a
DurationPipe,
LocationComponent,
SermonLargeComponent,
VideoServicesComponent,
AddSermonPopupComponent,
LoginPopupComponent,
OkPopupComponent,
@ -110,8 +130,20 @@ import { AddTransactionPageComponent } from './components/add-transaction-page/a
VideoPopupComponent,
CampPageComponent,
MembersPageComponent,
FCCPermitPageComponent,
BalloonComponent,
AddUserPopupComponent,
AddTransactionPageComponent
AddTransactionPageComponent,
AddTransactionPopupComponent,
MissionaryFormPageComponent,
ContributorYearlyReportComponent,
ContributorAllReportsComponent,
LiveStreamComponent,
ImageComponent,
YoutubeListComponent,
VideoComponent,
VideoItemComponent,
YoutubePopupComponent
],
imports: [
BrowserModule,
@ -128,9 +160,11 @@ import { AddTransactionPageComponent } from './components/add-transaction-page/a
MatSliderModule,
MatSnackBarModule,
MatDialogModule,
MatAutocompleteModule
MatAutocompleteModule,
MatRadioModule,
MatCheckboxModule
],
providers: [LoginService,UserService,GoogleAnalyticsService,SermonService,TransactionService,EventService,EmailService,WindowRefService],
providers: [LoginService,PrintService,UserService,GoogleAnalyticsService,SermonService,TransactionService,EventService,EmailService,MissionarySupportService,YoutubeListService,WindowRefService],
entryComponents: [AddSermonPopupComponent,
LoginPopupComponent,
OkPopupComponent,
@ -140,7 +174,9 @@ import { AddTransactionPageComponent } from './components/add-transaction-page/a
SharePopupComponent,
AddEventPopupComponent,
AddUserPopupComponent,
VideoPopupComponent],
VideoPopupComponent,
AddTransactionPopupComponent,
YoutubePopupComponent],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -1,72 +1,73 @@
.d-inline-block {
display: inline-block;
.b-0 {
border: 0;
}
.fw-b {
.fab-buttons {
display: none;
}
.fw-bold {
font-weight: bold;
}
.w-5 {
width: 5%;
.fs-1-2-5 {
font-size: 1.25rem;
}
.w-15 {
width: 15%;
.highlight {
background-color: #F3FAFF;
}
.w-20 {
width: 20%;
}
.w-30 {
width: 30%;
}
.w-75 {
width: 75%;
}
.w-80 {
width: 80%;
}
.m-1 {
margin: 1rem;
}
.mt-1 {
margin-top: 1rem;
}
.mt-2 {
margin-top: 2rem;
.highlight-yellow {
background-color: lightyellow;
}
.ml-1 {
margin-left: 1rem;
}
.ml-2 {
margin-left: 2rem;
}
.mr-1 {
margin-right: 1rem;
}
.pt-1 {
padding-top: 1rem;
.p-0 {
padding: 0;
}
.bb-1 {
border-bottom: 1px solid #ff4081;
}
table {
border-collapse: collapse;
width: 100%;
}
td, th {
padding: 5px;
border: 1px solid orange;
border: 1px solid lightslategray;
}
.remove-top-border {
border-top: 0;
}
.remove-border {
border: 0;
}
.side-bar > button {
margin: 20px;
width: calc(100% - 40px);
}
#audio-player-filler{
height: 60px;
background-color: inherit;
}
@media(max-width:800px){
.fab-buttons{
display: inline-block;
position: fixed;
bottom: 20px;
right: 20px;
}
}

View File

@ -1,118 +1,89 @@
<div>&nbsp;</div>
<h2 class="ml-1 mr-1 bb-1">Add Transactions</h2>
<secondary-page-component [fixedSideBar]="true">
<secondary-page-component [fixedSideBar]="true" >
<div mainContent>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div formArrayName="contributions" *ngFor="let item of form.get('contributions').controls; let i = index;">
<div class="m-1 mt-2 bb-1" [formGroupName]="i">
<button class="w-5" type="button" mat-icon-button (click)="deleteContributor(i)">
<i ofbicon>delete_forever</i>
</button>
<mat-form-field class="w-20">
<input matInput placeholder="Date" type="date" formControlName="date" >
</mat-form-field>
<mat-form-field class="w-75">
<input type="text" placeholder="Contributor" aria-label="Contributor" matInput formControlName="contributorId" (ngModelChange)="onContributorChange($event)" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="contirbutorDisplayFn">
<mat-option *ngFor="let option of filteredContributors" [value]="option">
{{option.display}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<div formArrayName="transactions" *ngFor="let trans of item.get('transactions').controls; let a = index;">
<div [formGroupName]="a">
<button class="w-5" type="button" mat-icon-button (click)="deleteTransaction(i,a)">
<i ofbicon>delete_forever</i>
</button>
<mat-form-field class="w-15">
<mat-select formControlName="typeId">
<mat-option *ngFor="let type of types" [value]="type.value" >
{{type.display}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="w-20">
<input matInput placeholder="Check Number" type="text" formControlName="checkNumber" >
</mat-form-field>
<mat-form-field class="w-20">
<mat-select formControlName="fundId">
<mat-option *ngFor="let fund of funds" [value]="fund.value">
{{fund.display}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="w-20">
<input matInput placeholder="Amount" type="number" formControlName="amount" >
</mat-form-field>
<mat-form-field class="w-20">
<input matInput placeholder="Description" type="text" formControlName="description" >
</mat-form-field>
</div>
</div>
<button class="w-15" mat-stroked-button type="button" (click)="addTransaction(i)">Add Transaction</button>
<div class="ml-1 w-20 d-inline-block">
{{contributorName(i)}}
</div>
<div class="ml-1 w-20 d-inline-block">
General: {{contributorTotal(i, 1)}}
</div>
<div class="w-15 d-inline-block">
Missions: {{contributorTotal(i, 2)}}
</div>
<div class="w-15 d-inline-block">
Total: {{contributorTotal(i, 0)}}
</div>
</div>
</div>
<button mat-stroked-button class="m-1 w-15" type="button" (click)="addContributor()">Add Contributor</button>
<div class="ml-1 w-20 d-inline-block">
Combined
</div>
<div class="ml-1 w-20 d-inline-block">
General: {{combinedTotal(1)}}
</div>
<div class="w-15 d-inline-block">
Missions: {{combinedTotal(2)}}
</div>
<div class="w-15 d-inline-block">
Total: {{combinedTotal(0)}}
</div>
<button mat-raised-button type="submit" [disabled]="!form.valid || submitButtonDisabled">{{ submitButtonText }}</button>
</form>
</div>
<div sideBar class="side-bar">
<table>
<thead>
<tr>
<th>General</th>
<th>Missions</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let c of form.get('contributions').value; let i = index">
<tr>
<td colspan="3" class="pt-1">{{contributorName(i)}}</td>
<ng-container *ngFor="let c of contributions; let ci = index">
<tr [class.bg-red]="!contributorDatesMatch(c.contributorId)">
<td colspan="2" class="fw-bold fs-1-2-5">{{c.date}}</td>
<td colspan="4" class="fw-bold fs-1-2-5">{{contributorName(c.contributorId)}}</td>
</tr>
<tr>
<td>{{contributorTotal(i, 1) | currency}}</td>
<td>{{contributorTotal(i, 2) | currency}}</td>
<td>{{contributorTotal(i, 0) | currency}}</td>
<td></td>
<td class="fw-bold">Date</td>
<td class="fw-bold">Type</td>
<td class="fw-bold">Check</td>
<td class="fw-bold">Fund</td>
<td class="fw-bold">Amount</td>
</tr>
<ng-container *ngFor="let t of c.transactions; let ti = index">
<tr [class.highlight]="ti % 2">
<td rowspan="2" style="width: 30px;">
<button mat-icon-button (click)="deleteTransaction(t.contributorId, ti)"><i ofbicon>delete_forever</i></button>
</td>
<td colspan="5" *ngIf="t.description && t.description.length > 0">{{t.description}}</td>
<td colspan="5" *ngIf="!t.description || t.description.length === 0" class="b-0 p-0"></td>
</tr>
<tr [class.highlight]="ti % 2">
<td [class.remove-top-border]="!t.description || t.description.length === 0">{{t.date}}</td>
<td [class.remove-top-border]="!t.description || t.description.length === 0">{{transactionType[t.typeId]}}</td>
<td [class.remove-top-border]="!t.description || t.description.length === 0">{{t.checkNumber}}</td>
<td [class.remove-top-border]="!t.description || t.description.length === 0">{{fund[t.fundId]}}</td>
<td [class.remove-top-border]="!t.description || t.description.length === 0">{{t.amount | currency}}</td>
</tr>
</ng-container>
<tr>
<td></td>
<td></td>
<td></td>
<td class="fw-bold">General</td>
<td class="fw-bold">Missions</td>
<td class="fw-bold">Total</td>
</tr>
<tr class="highlight-yellow">
<td></td>
<td></td>
<td></td>
<td>{{contributorTotal(c.contributorId, 1) | currency}}</td>
<td>{{contributorTotal(c.contributorId, 2) | currency}}</td>
<td>{{contributorTotal(c.contributorId, 0) | currency}}</td>
</tr>
<tr>
<td colspan="5">
<button mat-raised-button (click)="addTransaction(c.contributorId)" >Add Transaction to {{contributorName(c.contributorId)}}</button>
</td>
</tr>
</ng-container>
<tr>
<td colspan="3" class="pt-1">
Total
</td>
<td colspan="6" class="fw-bold fs-1-2-5">Grand Totals</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td class="fw-bold">General</td>
<td class="fw-bold">Missions</td>
<td class="fw-bold">Total</td>
</tr>
<tr class="highlight-yellow">
<td></td>
<td></td>
<td></td>
<td>{{combinedTotal(1) | currency}}</td>
<td>{{combinedTotal(2) | currency}}</td>
<td>{{combinedTotal(0) | currency}}</td>
</tr>
</tbody>
</table>
<div class="fab-buttons" >
<button mat-fab (click)="addTransaction(-1)"><i ofbicon>add</i></button>
</div>
</div>
<div sideBar class="side-bar">
<button mat-raised-button (click)="addTransaction(-1)" >Add Transaction</button>
<button mat-raised-button (click)="submit()" [disabled]="contributions.length === 0 || submitButtonDisabled" >{{submitButtonText}}</button>
</div>
</secondary-page-component>

View File

@ -1,11 +1,13 @@
import { Component, OnInit } from '@angular/core';
import { Transaction } from './transaction';
import { UserService } from 'src/app/services/user.service';
import { UserService } from '../../services/user.service';
import { Contribution } from './contribution';
import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
import { checkNumberValidator } from './check-number-validator';
import { contributorValidator } from './contributor-validator';
import { TransactionService } from 'src/app/services/transaction.service';
import { TransactionService } from '../../services/transaction.service';
import { MatDialog } from '@angular/material';
import { AddTransactionPopupComponent, ValDisplay } from './add-transaction-popup/add-transaction-popup.component';
import { TransactionType } from './transaction-type';
import { Fund } from './fund';
import { OkPopupComponent } from '../popups/ok-popup/ok-popup.component';
@Component({
selector: 'app-add-transaction-page',
@ -16,114 +18,115 @@ export class AddTransactionPageComponent implements OnInit {
errorMessages = [];
form: FormGroup;
contributions: Contribution[] = [];
submitButtonDisabled: boolean = false;
submitButtonText: string = "Submit";
types: {value:number,display:string}[] = [];
funds: {value:number,display:string}[] = [];
submitButtonText: string = "Submit Transactions";
transactionType = TransactionType;
fund = Fund;
transactionTypes: ValDisplay[] = [];
funds: ValDisplay[] = [];
contributors: {value:number,display:string}[] = [];
filteredContributors: {value:number,display:string}[]
constructor(private userService: UserService, private formBuilder: FormBuilder, private transactionService: TransactionService) {
this.form = this.formBuilder.group({
contributions: this.formBuilder.array([this.getContribution()])
});
constructor(private dialogService: MatDialog, private userService: UserService, private transactionService: TransactionService) {
for(let m in TransactionType) {
if (typeof TransactionType[m] === 'number') {
this.transactionTypes.push({value:<any>TransactionType[m], display: m});
}
}
for(let m in Fund) {
if (typeof Fund[m] === 'number') {
this.funds.push({value:<any>Fund[m], display: m});
}
}
}
ngOnInit() {
this.types = [{value:1, display: 'Cash'},{value:2, display: 'Check'}];
this.funds = [{value:1, display: 'General'},{value:2, display: 'Missions'}];
ngOnInit() {
this.userService.getAll().subscribe(res => this.contributors = (<any>res).users);
}
getContribution(): FormGroup {
const date = new Date();
const dte = date.getFullYear() + "-" + (date.getMonth()<10?'0':'') + (date.getMonth() + 1) + "-" + (date.getDate()<10?'0':'') + date.getDate();
return this.formBuilder.group({
date: [dte, Validators.required],
contributorId: [0, [Validators.required, contributorValidator(this)]],
transactions: this.formBuilder.array([this.getTransaction()])
});
}
getTransaction(): FormGroup {
return this.formBuilder.group({
amount: [null, Validators.required],
checkNumber: [''],
description: [''],
fundId: [1, [Validators.required, Validators.min(1), Validators.max(2)]],
taxYear: [(new Date()).getFullYear(), Validators.required],
typeId: [1, [Validators.required, Validators.min(1), Validators.max(2)]],
}, { validators: checkNumberValidator });
}
addTransaction(contributorId: number) {
const contrib = this.contributors.find(c => c.value === contributorId);
const contributor = this.contributions.find(c => c.contributorId === contributorId);
const lastTransaction = contributor && contributor.transactions.length > 0 ? contributor.transactions[contributor.transactions.length-1] : null;
onContributorChange(event) {
if (event && event.value) {
if (event.value === -1) {
// Open new user dialog
const ref = this.dialogService.open(AddTransactionPopupComponent, {
data: {
contributors: this.contributors,
funds: this.funds,
types: this.transactionTypes,
contributor: contrib ? contrib : undefined,
typeId: lastTransaction ? lastTransaction.typeId : TransactionType.Cash,
fundId: lastTransaction ? (lastTransaction.fundId === Fund.General ? Fund.Missions : Fund.General) : Fund.General,
checkNumber: lastTransaction ? lastTransaction.checkNumber : '',
goodsOrServices: false
}
event = event.display;
}
const filterValue = event.toLowerCase();
this.filteredContributors = this.contributors.filter(option => option.display.toLowerCase().indexOf(filterValue) >= 0);
if (this.filteredContributors.length === 0) {
this.filteredContributors.push({value:-1, display:'Add New Contributor'});
});
ref.afterClosed().subscribe((res: {transaction:Transaction,saveAndAdd:boolean}) => {
if (res) {
let contrib = this.contributions.find(c => c.contributorId === res.transaction.contributorId);
if (!contrib) {
contrib = new Contribution();
contrib.contributorId = res.transaction.contributorId;
contrib.date = res.transaction.date;
contrib.transactions = [];
this.contributions.push(contrib);
}
res.transaction.taxYear = +res.transaction.date.split('-')[0];
contrib.transactions.push(res.transaction);
if (res.saveAndAdd === true) {
this.addTransaction(contrib.contributorId);
}
}
});
}
deleteContributor(contributorId: number) {
const contrib = this.contributions.findIndex(c => c.contributorId === contributorId);
if (contrib >= 0) {
this.contributions.splice(contrib, 1);
}
}
contirbutorDisplayFn(contributor?: {value:number,display:string}): string | undefined {
return contributor ? contributor.display : undefined;
}
addTransaction(contributorPosition: number) {
const contributors = this.form.get('contributions') as FormArray;
const transactions = contributors.controls[contributorPosition].get('transactions') as FormArray;
const lastTransaction = transactions.value.length > 0 ? transactions.value[transactions.value.length-1] : undefined;
console.log(lastTransaction);
const newTransaction = this.getTransaction();
if (lastTransaction) {
newTransaction.patchValue({
"typeId": lastTransaction.typeId,
"checkNumber": lastTransaction.checkNumber,
"fundId": lastTransaction.fundId === 1 ? 2 : 1
});
}
transactions.push(newTransaction);
}
addContributor() {
const contributors = this.form.get('contributions') as FormArray;
contributors.push(this.getContribution());
}
deleteContributor(index: number) {
const contributors = this.form.get('contributions') as FormArray;
contributors.removeAt(index);
}
deleteTransaction(contributorIndex: number, transactionIndex: number) {
const contributors = this.form.get('contributions') as FormArray;
const transactions = contributors.controls[contributorIndex].get('transactions') as FormArray;
transactions.removeAt(transactionIndex);
}
contributorName(index: number) {
const contributors = this.form.get('contributions') as FormArray;
const contributor = contributors.controls[index].get('contributorId');
if (contributor && contributor.value.display) {
return contributor.value.display;
deleteTransaction(contributorId: number, transactionIndex: number) {
const contrib = this.contributions.find(c => c.contributorId === contributorId);
if (contrib) {
contrib.transactions.splice(transactionIndex, 1);
}
if (contrib.transactions.length === 0) {
this.deleteContributor(contributorId);
}
}
contributorTotal(index: number, fundId: number) {
const contributors = this.form.get('contributions') as FormArray;
const transactions = contributors.controls[index].get('transactions').value;
contributorName(id: number) {
const contributor = this.contributors.find(c => c.value === id);
return contributor ? contributor.display : undefined;
}
contributorDatesMatch(contributorId: number) {
const contributor = this.contributions.find(c => c.contributorId === contributorId);
if (!contributor) return true;
let match = true;
contributor.transactions.forEach(t => {
if (t.date != contributor.date) {
match = false;
}
});
return match;
}
contributorTotal(contributorId: number, fundId: number) {
const contrib = this.contributions.find(c => c.contributorId === contributorId);
if (!contrib) return 0;
var sum = 0;
transactions.forEach(e => {
contrib.transactions.forEach(e => {
if (e.fundId === fundId || fundId === 0) {
sum += e.amount;
}
@ -132,9 +135,8 @@ export class AddTransactionPageComponent implements OnInit {
}
combinedTotal(fundId: number) {
const contributors = this.form.get('contributions').value;
var sum = 0;
contributors.forEach(c => {
this.contributions.forEach(c => {
c.transactions.forEach(t => {
if (t.fundId === fundId || fundId === 0) {
sum += +t.amount;
@ -144,25 +146,29 @@ export class AddTransactionPageComponent implements OnInit {
return sum;
}
onSubmit() {
submit() {
this.submitButtonText = 'Submitting...';
this.submitButtonDisabled = true;
var data = this.form.value;
const transactions: Transaction[] = [];
for(let c = 0; c < data.contributions.length; c++) {
const contribution = data.contributions[c] as Contribution;
for(let t = 0; t < contribution.transactions.length; t++) {
const transaction = contribution.transactions[t] as Transaction;
transaction.contributorId = (<any>contribution.contributorId).value;
transaction.date = contribution.date;
transactions.push(transaction);
for(let c = 0; c < this.contributions.length; c++) {
for(let t = 0; t < this.contributions[c].transactions.length; t++) {
transactions.push(this.contributions[c].transactions[t]);
}
}
console.log(transactions);
this.transactionService.createFromArray(transactions).subscribe(x =>{
this.submitButtonText = 'Submit';
this.submitButtonText = 'Submit Transactions';
this.submitButtonDisabled = false;
}, e => console.log(e));
this.contributions = [];
}, e => {
this.dialogService.open(OkPopupComponent,{
data: {
title: 'Error Saving Transactions',
message: e.toString()
}
});
});
}
}

View File

@ -0,0 +1,11 @@
.w-100 {
width: 100%;
}
.w-50 {
width: 50%;
}
.m-5 {
margin: 5px;
}

View File

@ -0,0 +1,54 @@
<div md-dialog-title>
<p>Add Transaction</p>
</div>
<div md-dialog-content>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<mat-form-field>
<input matInput placeholder="Date" type="date" formControlName="date" >
</mat-form-field>
<mat-form-field>
<input type="text" placeholder="Contributor" aria-label="Contributor" matInput formControlName="contributor" (ngModelChange)="onContributorChange($event)" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="contributorDisplayFn">
<mat-option *ngFor="let option of filteredContributors" [value]="option">
{{option.display}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<button mat-button class="m-5" type="button" (click)="addUser()">Add</button>
<br>
<mat-form-field class="w-50">
<mat-select formControlName="typeId">
<mat-option *ngFor="let type of types" [value]="type.value" >
{{type.display}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="w-50">
<input matInput placeholder="Check Number" type="text" formControlName="checkNumber" >
</mat-form-field>
<div></div>
<mat-form-field class="w-50">
<mat-select formControlName="fundId">
<mat-option *ngFor="let fund of funds" [value]="fund.value">
{{fund.display}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="w-50">
<input matInput placeholder="Amount" type="number" formControlName="amount" >
</mat-form-field>
<br>
<mat-form-field class="w-100">
<input matInput placeholder="Description" type="text" formControlName="description" >
</mat-form-field>
<br>
<mat-checkbox formControlName="goodsOrServices">Goods Or Services Received?</mat-checkbox>
<br>
<br>
<button mat-raised-button class="m-5" type="button" (click)="cancel()" >Cancel</button>
<button mat-raised-button class="m-5" type="submit" [disabled]="!form.valid || saveBtnDisabled">{{ saveBtnTxt }}</button>
<button mat-raised-button class="m-5" type="button" (click)="onSubmit(true)" [disabled]="!form.valid || saveBtnDisabled">{{ saveAndAddBtnTxt }}</button>
</form>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AddTransactionPopupComponent } from './add-transaction-popup.component';
describe('AddTransactionPopupComponent', () => {
let component: AddTransactionPopupComponent;
let fixture: ComponentFixture<AddTransactionPopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AddTransactionPopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AddTransactionPopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,120 @@
import { Component, OnInit, Inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef, MatDialog } from '@angular/material';
import { contributorValidator } from '../contributor-validator';
import { Transaction } from '../transaction';
import { AddUserPopupComponent } from '../../popups/add-user-popup/add-user-popup.component';
export interface ValDisplay {
value: number;
display: string;
}
export interface DialogData {
contributors: ValDisplay[],
funds: ValDisplay[],
types: ValDisplay[],
contributor: ValDisplay,
date: string,
typeId: number,
fundId: number,
checkNumber: string,
description: string,
taxYear: number,
goodsOrServices: boolean
}
@Component({
selector: 'app-add-transaction-popup',
templateUrl: './add-transaction-popup.component.html',
styleUrls: ['./add-transaction-popup.component.css']
})
export class AddTransactionPopupComponent implements OnInit {
form: FormGroup;
saveBtnDisabled: boolean = false;
saveBtnTxt: string = 'Save';
saveAndAddBtnTxt: string = 'Save & Add';
contributors: ValDisplay[] = [];
filteredContributors: ValDisplay[]
funds: ValDisplay[];
types: ValDisplay[];
constructor(private dialogService: MatDialog, private formBuilder: FormBuilder, @Inject(MAT_DIALOG_DATA) public data: DialogData, private dialogRef:MatDialogRef<AddTransactionPopupComponent>) {
this.contributors = this.data.contributors || [];
this.funds = this.data.funds || [];
this.types = this.data.types || [];
const date = new Date();
const dte = date.getFullYear() + "-" + (date.getMonth()<10?'0':'') + (date.getMonth() + 1) + "-" + (date.getDate()<10?'0':'') + date.getDate();
this.form = this.formBuilder.group({
date: [this.data.date || dte, [Validators.required]],
contributor: [this.data.contributor, [Validators.required, contributorValidator(this.contributors)]],
typeId: [this.data.typeId || 1, [Validators.required]],
fundId: [this.data.fundId || 1, [Validators.required]],
checkNumber: [this.data.checkNumber],
description: [this.data.description],
amount: ['', [Validators.required]],
taxYear: [this.data.taxYear || new Date().getFullYear(), [Validators.required]],
goodsOrServices: [this.data.goodsOrServices || false]
});
}
ngOnInit() {
}
contributorDisplayFn(contributor?: {value:number,display:string}): string | undefined {
return contributor ? contributor.display : undefined;
}
onContributorChange(event) {
if (event && event.value) {
if (event.value === -1) {
// Open new user dialog
}
event = event.display;
}
const filterValue = event.toLowerCase();
this.filteredContributors = this.contributors.filter(option => option.display.toLowerCase().indexOf(filterValue) >= 0);
if (this.filteredContributors.length === 0) {
this.filteredContributors.push({value:-1, display:'Add New Contributor'});
}
}
onSubmit(saveAndAdd: boolean = false) {
const trans = new Transaction();
trans.amount = this.form.value.amount;
trans.checkNumber = this.form.value.checkNumber;
trans.contributorId = this.form.value.contributor.value;
trans.date = this.form.value.date;
trans.description = this.form.value.description;
trans.fundId = this.form.value.fundId;
trans.taxYear = this.form.value.taxYear;
trans.typeId = this.form.value.typeId;
trans.goodsOrServices = this.form.value.goodsOrServices;
this.dialogRef.close({transaction:trans,saveAndAdd:saveAndAdd});
}
cancel() {
this.dialogRef.close();
}
addUser() {
const ref = this.dialogService.open(AddUserPopupComponent);
ref.afterClosed().subscribe((res: any) => {
if (res && res.id > 0) {
this.contributors.push({ value: res.id, display: res.lastName + ' ' + res.firstName });
let contrib = this.contributors.find(c => c.value === res.id);
this.form.get("contributor").setValue(contrib);
}
});
}
}

View File

@ -1,14 +1,14 @@
import { ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms';
import { AddTransactionPageComponent } from './add-transaction-page.component';
export function contributorValidator(comp: AddTransactionPageComponent): ValidatorFn {
export function contributorValidator(contributors: {value:number,display:string}[]): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const val = control.value;
const error = {'invalidName':{value: val}};
if (!val || !val.value) {
return error;
}
const contributor = comp.contributors.findIndex(c => c.value === val.value);
const contributor = contributors.findIndex(c => c.value === val.value);
if (contributor >= 0) {
return null;
}

View File

@ -0,0 +1,4 @@
export enum Fund {
General = 1,
Missions = 2
}

View File

@ -0,0 +1,4 @@
export enum TransactionType {
Cash = 1,
Check = 2
}

View File

@ -7,4 +7,5 @@ export class Transaction {
fundId: number;
taxYear: number;
typeId: number;
goodsOrServices: boolean;
}

View File

@ -1,6 +1,6 @@
header{
div.header{
position:fixed;
top:0;
left:0;
@ -51,7 +51,7 @@ header{
padding-bottom: 140px;
}
footer{
div.footer{
background-color: rgb(50,50,50);
padding-top: 20px;
padding-bottom: 20px;
@ -81,7 +81,7 @@ footer{
}
@media(max-width: 800px){
footer{
div.footer{
height: 380px;
}
.footer-panel{

View File

@ -1,6 +1,6 @@
<header>
<div class="header" [class.print-hide]="printService.isPrinting">
<div id="header-background" [ngStyle]="{'opacity':headerOpacity}"></div>
<button mat-button class="header-button-home" routerLink="/home"><img id="logo" src="assets/images/tiny/logo.png" height="30" alt="logo"> Old Fashion Baptist</button>
<span class="hideOnMobile"></span>
@ -12,13 +12,13 @@
(click)="mainMenuClick()">
<i ofbicon class="example-icon" style="line-height:20px;">menu</i>
</button>
</header>
</div>
<div class="content">
<div class="content" [class.print-hide-children]="printService.isPrinting">
<router-outlet></router-outlet>
</div>
<footer>
<div class="footer" [class.print-hide]="printService.isPrinting">
<div id="footer-content">
<div id="footer-contact-content" class="footer-panel">
<a target="_blank" href="https://www.google.com/maps/place/Old+Fashion+Baptist+Church/@45.9814004,-112.5320574,11.87z/data=!4m12!1m6!3m5!1s0x535b078c3c74ea33:0xac299097142c5894!2sOld+Fashion+Baptist+Church!8m2!3d45.951287!4d-112.511978!3m4!1s0x535b078c3c74ea33:0xac299097142c5894!8m2!3d45.951287!4d-112.511978">
@ -27,8 +27,9 @@
</a>
<p><a href="tel:+1-406-494-5028" class="phone">(406) 494 - 5028</a></p>
<p>Pastor Derek Loewen</p>
<p>Pastor Emeritus Ron Derksen</p>
<br>
</div>
<div id="footer-services-content" class="footer-panel">
<p>Sunday School: 10AM</p>
<p>Sunday Worship: 11AM</p>
@ -40,14 +41,14 @@
<p><i ofbicon class="copyright">copyright</i> Copyright {{copyrightYear}}</p>
<p>Old Fashion Baptist</p>
<p><a routerLink="/home">ofbbutte.com</a></p>
<p>Powered by God</p>
<p><a href="https://publicfiles.fcc.gov/fm-profile/KFGL" target="_blank">KFGL Public Information File</a></p>
<br>
</div>
</div>
<div class="audio-player-filler" [hidden]="!showAudioPlayer" ></div>
</footer>
</div>
<div class="audio-player" [class.audio-player-slide-up]="showAudioPlayer">
<div class="audio-player" [class.audio-player-slide-up]="showAudioPlayer" [class.print-hide]="printService.isPrinting">
<audio-player-component (closed)="audioPlayerClosed()" (started)="audioPlayerStarted()"></audio-player-component>
</div>

View File

@ -4,6 +4,7 @@ import { Router, NavigationEnd, Event } from '@angular/router';
import { EventService } from '../../services/event.service';
import { AudioPlayerService } from '../../services/audio-player.service';
import { BibleVerseService } from '../../services/bible-verse.service';
import { PrintService } from '../../services/print-service';
@Injectable()
@ -26,7 +27,8 @@ export class AppComponent {
constructor(private router: Router,
private audioPlayerService: AudioPlayerService,
private googleAnalyticsService: GoogleAnalyticsService){
private googleAnalyticsService: GoogleAnalyticsService,
public printService: PrintService){
this.router.events.subscribe((event:Event) => {
if(event instanceof NavigationEnd) {
this.lastRoute = this.currRoute;

View File

@ -0,0 +1,23 @@
.full-width{
width: 100%;
}
.hide{
display: none;
}
.bold {
font-weight: bold;
}
.margin-top-space {
margin-top: 20px;
}
.errorMessages{
color: white;
background-color: rgb(255,90,90);
padding: 10px;
border-radius: 3px;
margin-bottom: 5px;
}

View File

@ -0,0 +1,49 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent>
<br>
<p class="bold">
We are so glad you found one of our balloons. We would love to hear from you
and where you found it!
</p>
<p class="margin-top-space">
Click <a routerLink="/whoweare">here</a> to learn more about us.
</p>
<p class="margin-top-space">
Click <a routerLink="/salvation">here</a> to learn more about <span class="bold">Jesus</span>.
</p>
<br>
<div *ngIf="!formSubmitted">
<form class="form" #contactForm="ngForm" (ngSubmit)="onSubmit()">
<mat-form-field class="full-width">
<input matInput type="text" placeholder="Name" required value="" [(ngModel)]="name" name="name" >
</mat-form-field>
<mat-form-field class="full-width">
<input matInput type="text" placeholder="City, State" required value="" [(ngModel)]="location" name="location" >
</mat-form-field>
<mat-form-field class="full-width">
<input matInput type="email" placeholder="Email" value="" [(ngModel)]="email" name="email" pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$">
</mat-form-field>
<mat-form-field class="full-width">
<input matInput type="tel" placeholder="Phone" value="" [(ngModel)]="phone" name="phone">
</mat-form-field>
<mat-form-field class="full-width">
<textarea matInput type="text" placeholder="Other Information or Questions" value="" [(ngModel)]="body" name="body" rows="5" ></textarea>
</mat-form-field>
<mat-form-field class="hide">
<input matInput type="text" placeholder="hp" required value="" [(ngModel)]="hp" name="subject">
</mat-form-field>
<div class="errorMessages" *ngIf="errorMessages.length > 0">
<p *ngFor="let error of errorMessages">{{error}}</p>
</div>
<button mat-raised-button type="submit" [disabled]="!contactForm.form.valid || submitButtonDisabled">{{submitButtonText}}</button>
</form>
</div>
<div *ngIf="formSubmitted">
<p><b>Thank You!</b></p>
<p>Your message has been sent.</p>
</div>
</div>
<div sideBar ofbFadeInOnScroll>
</div>
</secondary-page-component>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BalloonComponent } from './balloon.component';
describe('BalloonComponent', () => {
let component: BalloonComponent;
let fixture: ComponentFixture<BalloonComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ BalloonComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BalloonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,84 @@
import { Router } from '@angular/router';
import { MatDialogConfig } from '@angular/material';
import { OkPopupComponent } from '../popups/ok-popup/ok-popup.component';
import { EmailService } from '../../services/email.service';
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material';
@Component({
selector: 'app-contact-page',
templateUrl: './balloon.component.html',
styleUrls: ['./balloon.component.css']
})
export class BalloonComponent implements OnInit {
public submitButtonText: string = "Submit";
public submitButtonDisabled: boolean = false;
public formSubmitted: boolean = false;
public name: string;
public email: string;
public location: string;
public phone: string;
public body: string;
public hp: string = ".";
public errorMessages: string[] = [];
constructor(private emailService: EmailService,
private MatDialog: MatDialog,
private router: Router) { }
ngOnInit() {
}
onSubmit(){
this.errorMessages = [];
if (this.name == null || this.name == ""){
this.errorMessages.push("Please enter a name");
}
if (this.location == null || this.location == ""){
this.errorMessages.push("Please enter a city and state");
}
if (this.errorMessages.length > 0){ return; }
const body = `Location of balloon: ${this.location}<br>${this.body || ''}`;
let email = this.email;
if (!email || email.length === 0) {
email = 'N/A';
}
this.submitButtonText = "Please Wait...";
this.submitButtonDisabled = true;
this.emailService.sendEmail(this.name,
email,
this.phone || '',
body,
this.hp)
.subscribe(
success => {this.emailSuccess();},
error => {this.emailError();});
}
private emailSuccess(){
let opts = new MatDialogConfig;
opts.data = { title:'Email Sent','message':'Thank You! Your message has been sent.' };
let popup = this.MatDialog.open(OkPopupComponent,opts);
this.submitButtonText = "Submit";
this.submitButtonDisabled = false;
popup.afterClosed().subscribe(()=>{
this.formSubmitted = true;
});
}
private emailError(){
console.error("error");
let opts = new MatDialogConfig;
opts.data = { title:'Email Error','message':'Please make sure that you have entered a valid email address.' };
let popup = this.MatDialog.open(OkPopupComponent,opts);
this.submitButtonText = "Submit";
this.submitButtonDisabled = false;
}
}

View File

@ -1,5 +1,10 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent>
<br>
<p>
For general questions please complete the form below. If you are a missionary requesting
support please complete the <a routerLink="/missionary">Missionary Questionnaire form by clicking here.</a>
</p>
<br>
<div *ngIf="!formSubmitted">
<form class="form" #contactForm="ngForm" (ngSubmit)="onSubmit()">

View File

@ -0,0 +1,5 @@
@media print {
:host {
display: block;
}
}

View File

@ -0,0 +1,3 @@
<app-contributor-yearly-report *ngFor="let c of contributors; let i = index" [index]="i" [contributorTransactions]="c">
</app-contributor-yearly-report>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ContributorAllReportsComponent } from './contributor-all-reports.component';
describe('ContributorAllReportsComponent', () => {
let component: ContributorAllReportsComponent;
let fixture: ComponentFixture<ContributorAllReportsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ContributorAllReportsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ContributorAllReportsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,50 @@
import { Component, OnInit } from '@angular/core';
import { PrintService } from 'src/app/services/print-service';
import { UserService } from 'src/app/services/user.service';
import { TransactionService } from 'src/app/services/transaction.service';
import { forkJoin } from 'rxjs';
@Component({
selector: 'app-contributor-all-reports',
templateUrl: './contributor-all-reports.component.html',
styleUrls: ['./contributor-all-reports.component.css']
})
export class ContributorAllReportsComponent implements OnInit {
public contributors: {contributor:{}, transactions:[]}[] = [];
constructor(private printService: PrintService, private userService: UserService, private transactionService: TransactionService) { }
ngOnInit() {
const taxYear = 2024;
const query = forkJoin([this.userService.getAll(), this.transactionService.getByYear(taxYear), this.transactionService.getByYear(taxYear - 1)]);
query.subscribe(res => this.setup(res[0], res[1], res[2]));
this.printService.setPrinting(true);
}
private setup(contributorResult, transactionResult, priorYearTransactionResult) {
const contributors = contributorResult.users;
const transactions = transactionResult.transactions;
const priorYearTransactions = priorYearTransactionResult.transactions;
const contrib = {};
transactions.forEach(t => {
if (contrib.hasOwnProperty(t.contributorId)) {
contrib[t.contributorId].transactions.push(t);
} else {
const con = contributors.find(c => c.id === t.contributorId);
if (con) {
contrib[con.id] = {contributor: con, transactions: [t]};
contrib[con.id].priorYearTransactions = priorYearTransactions.filter(t => t.contributorId === con.id);
this.contributors.push(contrib[con.id]);
} else {
console.error('coould not find contributor for', t);
}
}
});
}
}

View File

@ -0,0 +1,140 @@
@media screen {
:host {
display: none;
}
}
@media print {
:host {
display: block;
margin-top: -50px;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
tr.page-break-after:after { content:""; display: block; page-break-before: always; }
}
.h-100 {
height: 300px;
}
.w-100 {
width: 100%;
}
.d-block {
display: block;
}
.flex {
display: flex;
}
.flex-grow-1 {
flex-grow: 1;
}
.flex-direction-column {
flex-direction: column;
}
.flex-align-center {
align-items: center;
}
.flex-justify-space-between {
justify-content: space-between;
}
.flex-align-space-between {
align-content: space-between;
}
.flex-align-top {
align-items: start;
}
.flex-align-stretch {
align-items: stretch;
}
.flex-justify-center {
justify-content: center;
}
.flex-justify-space-between {
justify-content: space-between;
}
hr {
border-top: 1px solid green;
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.text-left {
text-align: left;
}
.mt-5 {
margin-top: 5px;
}
.mt-20 {
margin-top: 20px;
}
.mb-20 {
margin-bottom: 20px;
}
.mr-40 {
margin-right: 40px;
}
.flex-direction-column {
flex-direction: column;
}
.page-break-before {
page-break-before: always;
}
.fw-bold {
font-weight: bold;
}
.hilight-yellow {
background-color: yellow;
}
table.borders {
border-collapse: collapse;
}
table.borders th, table.borders tr.row td {
border: 1px solid black;
}
table.cell-padding-5 td, table.cell-padding-5 th {
padding: 5px;
}
table.highlight-even tr.row:nth-child(even) {
background-color: #e6ffe6;
}
table tbody tr.footer td {
font-size: small;
vertical-align: bottom;
}
.text-align-right {
text-align: right;
}

View File

@ -0,0 +1,121 @@
<div *ngIf="index > 0" class="page-break-before"></div>
<div class="h-100 flex flex-align-center flex-justify-center flex-direction-column">
<img src="../../../assets/images/original/logo_dark.png">
<p class="mt-5">
Old Fashion Baptist Church Giving Statement {{taxYear}}
</p>
<p class="mt-5">
{{contributorName}}
</p>
<p *ngIf="contributorStreet && contributorCity && contributorState && contributorZip">{{contributorStreet}}</p>
<p *ngIf="contributorStreet && contributorCity && contributorState && contributorZip">{{contributorCity}} {{contributorState}}, {{contributorZip}}</p>
</div>
<div class="page-break-before">
<div class="header flex flex-justify-space-between">
<div>
<img src="../../../assets/images/original/logo_dark.png">
</div>
<div class="text-right">
<p>Old Fashion Baptist Church</p>
<p>5003 Wynne Ave</p>
<p>Butte, MT 59701</p>
<p>(406) 494-5028</p>
<p>ofbbutte.com</p>
</div>
</div>
<hr class="hr mt-20">
<h2 class="text-center mt-20">Old Fashion Baptist Church Giving Statement {{taxYear}}</h2>
<hr class="mt-20">
<div class="flex flex-align-stretch flex-justify-space-between">
<div class="mt-20 mb-20 mr-40 flex-grow-1 flex flex-direction-column flex-justify-space-between">
<div class="mt-20">
<p><b>Name: </b>{{contributorName}}</p>
<p><b>Year: </b>{{taxYear}}</p>
</div>
<table class="cell-padding-5 borders w-100">
<thead>
<tr>
<th colspan="2">{{taxYear}} Giving Summary</th>
</tr>
</thead>
<tbody class="cell-padding-5">
<tr class="row">
<td class="fw-bold">General Fund</td>
<td class="text-right">{{totalGeneral() | currency}}</td>
</tr>
<tr class="row">
<td class="fw-bold">Missions Fund</td>
<td class="text-right">{{totalMissions() | currency}}</td>
</tr>
<tr class="row">
<td class="fw-bold">Total Giving</td>
<td class="text-right hilight-yellow">{{(totalGeneral() + totalMissions()) | currency}}</td>
</tr>
</tbody>
</table>
<div>
Thank you for your support. The information provided in this statement reflects
your gifts on record for {{taxYear}}.
No goods or services were provided in exchange for your contributions.
</div>
</div>
<div class="mt-20">
<canvas #chart width="400" height="300"></canvas>
</div>
</div>
</div>
<table class="highlight-even cell-padding-5 w-100 borders mt-20">
<thead>
<tr>
<th class="text-left">Date</th>
<th nowrap class="text-left">Payment Type</th>
<th nowrap class="text-left">Check Number</th>
<th class="text-left">Fund</th>
<th class="text-left">Description</th>
<th class="text-left">Amount</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let t of transactions; let i = index">
<tr class="row">
<td nowrap>{{t.date | date:'MM-dd-yyyy':'UTC'}}</td>
<td nowrap>{{t.typeId === 1 ? 'Cash' : (t.typeId === 2 ? 'Check' : 'NA')}}</td>
<td nowrap>{{t.checkNumber}}</td>
<td nowrap>{{t.fundId === 1 ? 'General' : (t.fundId === 2 ? 'Missions' : 'NA')}}</td>
<td width="99%">{{t.description}}</td>
<td nowrap class="text-align-right">{{t.amount | currency}}</td>
</tr>
<tr *ngIf="isFooter(i+1)" class="footer page-break-after">
<td colspan="6">
<div class="flex flex-justify-space-between">
<p>Old Fashion Baptist Church Giving Statement {{taxYear }}</p>
<p>Page {{calcPage(i+1)}} of {{pages}}</p>
</div>
</td>
<div></div><!--DIV IS HERE TO MAKE PAGE BREAKS WORK CORRECTLY-->
</tr>
</ng-container>
<ng-container *ngFor="let r of fillerRows; let i = index">
<tr>
<td nowrap>&nbsp;</td>
<td nowrap></td>
<td nowrap></td>
<td nowrap></td>
<td width="99%"></td>
<td nowrap></td>
</tr>
<tr *ngIf="i === fillerRows.length-1" class="footer">
<td colspan="6">
<div class="flex flex-justify-space-between">
<p>Old Fashion Baptist Church Giving Statement {{taxYear}}</p>
<p>Page {{pages}} of {{pages}}</p>
</div>
</td>
</tr>
</ng-container>
</tbody>
</table>
<div *ngIf="pagesOdd" class="page-break-before">&nbsp;</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ContributorYearlyReportComponent } from './contributor-yearly-report.component';
describe('ContributorYearlyReportComponent', () => {
let component: ContributorYearlyReportComponent;
let fixture: ComponentFixture<ContributorYearlyReportComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ContributorYearlyReportComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ContributorYearlyReportComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,158 @@
import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core';
import { PrintService } from '../../services/print-service';
import { Transaction } from '../add-transaction-page/transaction';
import * as Chart from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { CurrencyPipe } from '@angular/common';
@Component({
selector: 'app-contributor-yearly-report',
templateUrl: './contributor-yearly-report.component.html',
styleUrls: ['./contributor-yearly-report.component.css']
})
export class ContributorYearlyReportComponent implements OnInit {
@ViewChild('chart') chartElement:ElementRef;
@Input() index: number;
@Input() contributorTransactions: {contributor: {}, transactions: {}, priorYearTransactions: {}};
public transactions: Transaction[] = [];
public priorTransactions: Transaction[] = [];
public get contributorName(): string {
const first = (<any>this.contributorTransactions.contributor).firstName;
const last = (<any>this.contributorTransactions.contributor).lastName;
if (last && first) {
return last + ', ' + first;
} else if (last) {
return last;
} else if (first) {
return first;
} else {
return '';
}
}
public contributorStreet: string = '7878 Washington St';
public contributorCity: string = 'Butte';
public contributorState: string = 'MT';
public contributorZip: string = '59701';
public taxYear: number = 2024;
public rowsFirstPage: number = 11;
public rowsPerPage: number = 30;
public get fillerRows(): number[] {
const rowsToFillPage = this.rowsFirstPage + ((this.pages - 2) * this.rowsPerPage);
const rowsNeeded = rowsToFillPage - this.transactions.length;
return Array(rowsNeeded).fill(1).map((x,i)=>i)
}
public get pages(): number {
return Math.ceil((this.transactions.length - this.rowsFirstPage) / this.rowsPerPage) + 2;
}
public get pagesOdd(): boolean {
return !(this.pages % 2 == 0)
}
private currencyPipe: CurrencyPipe = new CurrencyPipe('en-US');
constructor(public printService: PrintService) { }
ngOnInit() {
this.contributorCity = (<any>this.contributorTransactions.contributor).city;
this.contributorState = (<any>this.contributorTransactions.contributor).state;
this.contributorStreet = (<any>this.contributorTransactions.contributor).street;
this.contributorZip = (<any>this.contributorTransactions.contributor).zip;
this.transactions = <any>this.contributorTransactions.transactions;
this.priorTransactions = <any>this.contributorTransactions.priorYearTransactions;
this.renderChart();
}
public totalGeneral() {
let sum = 0;
this.transactions.filter(t => t.fundId === 1).forEach(t => sum += t.amount);
return sum;
}
public priorGeneral() {
let sum = 0;
this.priorTransactions.filter(t => t.fundId === 1).forEach(t => sum += t.amount);
return sum;
}
public totalMissions() {
let sum = 0;
this.transactions.filter(t => t.fundId === 2).forEach(t => sum += t.amount);
return sum;
}
public priorMissions() {
let sum = 0;
this.priorTransactions.filter(t => t.fundId === 2).forEach(t => sum += t.amount);
return sum;
}
public calcPage(index: number): number {
const val = Math.max(index, this.rowsFirstPage);
return Math.ceil((val - this.rowsFirstPage) / this.rowsPerPage) + 2;
}
public isFooter(index: number): boolean {
if (index === this.rowsFirstPage) {
return true;
}
if (index === (this.rowsFirstPage + ((this.calcPage(index) - 2) * this.rowsPerPage))) {
return true;
}
return false;
}
private renderChart() {
const me = this;
var myChart = new Chart(this.chartElement.nativeElement, {
plugins: [ChartDataLabels],
type: 'bar',
data: {
labels: [(this.taxYear - 1).toString(), this.taxYear.toString()],
datasets: [{
label: 'General',
data: [this.priorGeneral(), this.totalGeneral()],
backgroundColor: 'rgba(54, 245, 162, 0.2)',
borderColor: 'rgba(54, 245, 162, 1)',
borderWidth: 1
}, {
label: 'Missions',
data: [this.priorMissions(), this.totalMissions()],
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options:{
scales: {
xAxes: [{
stacked: true
}],
yAxes: [{
stacked: true,
ticks: {
callback: function(value, index, values) {
return me.currencyPipe.transform(value, 'USD', 'symbol', '1.0-0');
}
}
}]
},
plugins:{
datalabels:{
font:{
weight: 'bold'
},
formatter: function(value, context){
if (value === 0) {
return '';
}
return me.currencyPipe.transform(value, 'USD', 'symbol', '1.0-2');
}
}
}
}
});
}
}

View File

@ -0,0 +1,23 @@
.section-header{
font-size: 1.2em;
font-weight: bold;
border-bottom: 1px solid gray;
margin-bottom: 10px;
}
a{
color: inherit;
}
a:visited{
color: inherit;
}
.break-word {
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
}

View File

@ -0,0 +1,16 @@
<secondary-page-component [hideSideBarOnMobile]="true">
<div mainContent>
<p ofbFadeInOnScroll class="section-header">FCC Noncommercial FM Radio Station Construction Permit</p>
<p ofbFadeInOnScroll class="section-paragraph">
On November 9, 2021, Old Fashion Baptist Church of Butte, applicant for a Noncommercial FM Radio Station on
88.1mhz, in Butte, MT, filed an application with the Federal Communications Commission for a new Noncommercial FM
Station. Members of the public wishing to view this application or obtain information about how to file comments and
petitions on the application can visit <a class="break-word" href="https://enterpriseefiling.fcc.gov/dataentry/views/public/nceDraftCopy?displayType=html&appKey=25076f917cce2f5b0
17cd34cdd450dee&id=25076f917cce2f5b017cd34cdd450dee&goBack=N">https://enterpriseefiling.fcc.gov/dataentry/views/public/nceDraftCopy?displayType=html&appKey=25076f917cce2f5b0
17cd34cdd450dee&id=25076f917cce2f5b017cd34cdd450dee&goBack=N</a>.
</p>
</div>
<div sideBar class="side-bar">
</div>
</secondary-page-component>

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-fcc-permit-page',
templateUrl: './fcc-permit-page.component.html',
styleUrls: ['./fcc-permit-page.component.css']
})
export class FCCPermitPageComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,151 @@
import { DateTime } from "luxon";
import { VideoServices, VideoService } from './video-services';
export class Countdown {
public days: number = 0;
public hours: number = 0;
public minutes: number = 0;
public seconds: number = 0;
public services: VideoService[] = [];
public showButton: boolean;
public showCounter: boolean = false;
public dateDisplay: string;
public dateDisplaySmall: string;
public get daysDisplay(): string {
return this.formatDisplay(this.days);
}
public get daysText(): string {
return this.days === 1 ? 'day ' : 'days';
}
public get hoursDisplay(): string {
return this.formatDisplay(this.hours);
}
public get hoursText(): string {
return this.hours === 1 ? 'hr ' : 'hrs';
}
public get minutesDisplay(): string {
return this.formatDisplay(this.minutes);
}
public get minutesText(): string {
return this.minutes === 1 ? 'min ' : 'mins';
}
public get secondsDisplay(): string {
return this.formatDisplay(this.seconds);
}
public get secondsText(): string {
return this.seconds === 1 ? 'sec ' : 'secs';
}
private clock;
constructor(services: VideoService[]) {
if (!services || services.length == 0) return;
this.services = services;
this.services.forEach(v => {
v.date = DateTime.fromISO(v.date, {zone: 'America/Denver'})
});
this.services.sort((a, b) => {
if (a.date < b.date) return -1;
if (a.date === b.date) return 0;
return 1;
});
this.updateClock();
this.startClock();
}
private resetToZero() {
this.days = 0;
this.hours = 0;
this.minutes = 0;
this.seconds = 0;
}
private updateClock() {
this.showCounter = true;
var now = DateTime.local();
var firstNonArchived = this.getFirstNonArchived(now);
if (!firstNonArchived) {
this.stopClock();
this.resetToZero();
this.showCounter = false;
this.dateDisplay = '';
this.dateDisplaySmall = '';
return;
}
var hourDiff = (<DateTime>firstNonArchived.date).diff(now, ['hours']);
if (hourDiff.hours >= 48) {
this.showCounter = false;
this.dateDisplay = '';
this.dateDisplaySmall = '';
return;
}
var secDiff = (<DateTime>firstNonArchived.date).diff(now, ['seconds']);
if (secDiff.seconds <= 0) {
this.showButton = true;
}
var diff = (<DateTime>firstNonArchived.date).diff(now, ['days', 'hours', 'minutes', 'seconds']);
this.days = diff.days;
this.hours = diff.hours;
this.minutes = diff.minutes;
this.seconds = Math.floor(diff.seconds);
this.dateDisplay = firstNonArchived.date.toLocaleString(DateTime.DATETIME_HUGE);
this.dateDisplaySmall = firstNonArchived.date.toLocaleString(DateTime.DATETIME_MED)
}
private startClock() {
this.clock = setInterval(this.updateClock.bind(this), 1000);
}
private stopClock() {
clearInterval(this.clock);
}
private getFirstNonArchived(now: DateTime): VideoService {
if (this.services.length === 0) return null;
const nonArchived = this.services.filter(s => s.archived === false);
if (!nonArchived || nonArchived.length === 0) return null;
return nonArchived[0];
}
private getNearestPastDate(now: DateTime): VideoService {
if (this.services.length === 0) return null;
const archived = this.services.filter(s => s.archived === false);
if (!archived || archived.length === 0) return null;
var now = now || DateTime.local();
var nearestIndex = archived.findIndex(s => s.date > now);
if (nearestIndex === 0) return null;
if (nearestIndex === -1) return archived[archived.length - 1];
return archived[nearestIndex - 1];
}
private getNearestFutureDate(now: DateTime): VideoService {
if (this.services.length === 0) return null;
var now = now || DateTime.local();
var nearest = this.services.find(s => s.date > now);
return nearest;
}
private formatDisplay(val: number): string {
if (val <= 0) {
return '00';
}
if (val < 10) {
return '0' + val.toString();
}
return val.toString();
}
}

View File

@ -7,21 +7,42 @@ a{
color: inherit;
}
a {
text-decoration: underline;
}
a:hover {
cursor: pointer;
}
a:visited{
color: inherit;
}
.bold {
font-weight: bold;
}
img{
width: 70%;
height: auto;
}
img.w-100pct {
width: 100%;
height: auto;
}
img.full {
width: 100%;
height: auto;
}
.color-white {
color: white;
}
#call-to-action-container {
text-align: center;
position: absolute;
@ -30,6 +51,128 @@ img.full {
bottom: 10%;
}
#live-stream-container {
text-align: left;
position: absolute;
left: 10%;
right: 0;
bottom: 20%;
}
#title > .live {
display: inline-block;
background-color: red;
color: white;
padding: 0px 7px;
border-radius: 10px;
}
#live-date {
color: white;
font-weight: bold;
margin-top: 5px;
}
#live-stream-container > #title {
font-size: 25pt;
font-weight: bold;
color: white;
font-family: Franklin Gothic Medium,Franklin Gothic,ITC Franklin Gothic,Arial,sans-serif;
}
#countdown-container {
text-decoration: none;
position: relative;
margin-top: 7px;
display: inline-block;
font-size: 30pt;
font-weight: bold;
border-radius: 10px;
background-color: rgba(255, 255, 255, 1);
padding: 15px 20px;
-webkit-box-shadow: inset 0px 0px 20px -15px rgba(0,0,0,1);
-moz-box-shadow: inset 0px 0px 20px -15px rgba(0,0,0,1);
box-shadow: inset 0px 0px 20px -15px rgba(0,0,0,1);
border: 1px solid green;
font-family: Consolas,monaco,monospace;
}
/*Button*/
#countdown-container.show-button {
background-color:#2dabf9;
color:#ffffff;
text-shadow:0px 1px 0px #263666;
}
#countdown-container.show-button:hover {
background-color:#0688fa;
}
#countdown-container.show-button:active {
position:relative;
top:1px;
}
/*End Button*/
#countdown-container .text {
font-size: 10pt;
}
.opacity-zero {
opacity: 0;
}
#live-button {
position: absolute;
font-size: 25pt;
width: 100%;
height: 100%;
}
@media(max-width:900px){
#live-stream-container > #title {
font-size: 18pt;
}
#countdown-container {
font-size: 20pt;
}
#live-button {
font-size: 20pt;
}
}
.display-sm {
display: none;
}
@media(max-width:600px){
#live-stream-container {
left: 5%;
right: 5%;
bottom: 5%
}
#live-stream-container > #title {
font-size: 13pt;
}
#countdown-container {
font-size: 13pt;
margin-top: 3px;
}
.display-none-sm {
display: none;
}
.display-sm {
display: block;
}
#live-date {
margin-top: 2px;
}
#live-button {
font-size: 18pt;
margin-top: -5px;
}
}
#action-button {
font-size: 16pt;
max-width: 100%;
@ -39,6 +182,18 @@ img.full {
margin-right: 10px;
}
.w-100 {
width: 100%;
}
.w-200 {
width: 200px;
}
.h-200 {
height: 200px;
}
.action{
font-weight: bold;
font-size: 20px;
@ -175,7 +330,9 @@ img.full {
margin: 10px;
}
img.m-100-lg {
margin-left: 100px;
}
@media(max-width: 850px){
.row-content-col-left, .row-content-col-right{
@ -187,5 +344,16 @@ img.full {
img.full {
width: 90%;
}
img.m-100-lg {
margin-left: 0;
}
img.w-100pct {
width: 65%;
}
}
@media(max-width: 600px) {
img.w-100pct {
width: 95%;
}
}

View File

@ -2,38 +2,112 @@
<img id="background-image" src="assets/images/home-images/tiny/sunset_b.jpg" alt="background image" width="100%">
<div id="filler">
<div id="live-stream-container" *ngIf="showCountdown && countdown && countdown.showCounter">
<div id="title">
<span class="live">VIDEO</span>
<span *ngIf="!countdown.showButton"> SERVICE STARTS IN</span>
<span *ngIf="countdown.showButton"> SERVICE AVAILABLE NOW</span>
</div>
<a id="countdown-container" routerLink="/live" [class.show-button]="countdown.showButton" >
<div id="live-button" [class.opacity-zero]="!countdown.showButton">
<i ofbicon class="mr-10" >live_tv</i>
Click to Watch
</div>
<div [class.opacity-zero]="countdown.showButton">
<span class="number">{{countdown.daysDisplay}}</span>
<span class="text">&nbsp;{{countdown.daysText}}</span>
<span class="number">&nbsp;{{countdown.hoursDisplay}}</span>
<span class="text">&nbsp;{{countdown.hoursText}}</span>
<span class="number">&nbsp;{{countdown.minutesDisplay}}</span>
<span class="text">&nbsp;{{countdown.minutesText}}</span>
<span class="number">&nbsp;{{countdown.secondsDisplay}}</span>
<span class="text">&nbsp;{{countdown.secondsText}}</span>
</div>
</a>
<div id="live-date" class="display-none-sm">
{{countdown.dateDisplay}}
</div>
<div id="live-date" class="display-sm">
{{countdown.dateDisplaySmall}}
</div>
</div>
<div id="call-to-action-container" *ngIf="showCallToAction">
<button id="action-button" routerLink="/camp" mat-raised-button color="accent"><i ofbicon class="mr-10">info_outline</i>2019 Youth Camp</button>
</div>
<a id="action-button" href="/assets/Reopening Church Guidelines.pdf" target="_blank" mat-raised-button color="accent"><i ofbicon class="mr-10 color-white">info_outline</i><span class="color-white">Reopening Guidelines</span></a>
</div>
</div>
<div id="content-wrapper">
<div class="row tint" *ngIf="showSpecial" >
<div style="text-align: center; position: relative; margin-bottom: 15px;" *ngIf="showVBS">
<div style="position: absolute; width: 100%; height: calc(65% + 15px); top: 35%; background-color: white;"></div>
<img src="assets/images/home-images/tiny/VBS_small.jpg" style="width:75%; max-width: 350px; position: relative; z-index: 1; box-shadow: 0 0 20px pink;" >
</div>
<div class="row tint" *ngIf="showVGC" >
<div class="row-content">
<div class="row-content-col-left">
<a href="assets/images/champ_flyer.jpg" download="champ_flyer.jpg"><img class="full" ofbFadeInOnScroll src="assets/images/home-images/tiny/champ.jpg" height="300"></a>
<div class="row-content-col-left center">
<img style="max-height:500px" ofbFadeInOnScroll src="assets/images/original/VictoryGospelCrusade.png" height="500">
</div>
<div class="row-content-col-right align-top">
<p ofbFadeInOnScroll class="row-content-header">Victory Gospel Crusade</p>
<br />
<p ofbFadeInOnScroll class="bold">
When
</p>
<p ofbFadeInOnScroll>
August 4 - 9 @ 7PM
<br />
August 11 - 15 @ 7 PM
</p>
<br>
<p ofbFadeInOnScroll class="bold">
Where
</p>
<p ofbFadeInOnScroll>
Behind Pizza Ranch
<br />
1839 Longfellow St, Butte, Mt
<br /><br />
<i ofbicon>directions</i> <a target="_blank" class="align-top" href="https://www.google.com/maps/place/1839+Longfellow+St,+Butte,+MT+59701/@45.9732501,-112.513401,17z/data=!3m1!4b1!4m6!3m5!1s0x535b07e062beaaf7:0x827616c9ef692eed!8m2!3d45.9732464!4d-112.5108261!16s%2Fg%2F11v03h9chf?entry=ttu">View Map</a>
</p>
<p ofbFadeInOnScroll class="action">
<i ofbicon>play_arrow</i> <a (click)="playVictoryGospelCrusade()" class="align-top">Click Here To Watch The Trailer</a>
</p>
</div>
</div>
</div>
<div class="row" *ngIf="showSpecial" >
<div class="row-content">
<div class="row-content-col-left center">
<img style="max-height:500px" ofbFadeInOnScroll src="assets/images/home-images/tiny/Cowboy Carnival.jpg" height="500">
</div>
<div class="row-content-col-right align-top center">
<p ofbFadeInOnScroll class="row-content-header">Champ the Smiling Trick Horse</p>
<p ofbFadeInOnScroll class="row-content-header">Cowboy Carnival</p>
<p ofbFadeInOnScroll>
Come join us for this special event happening September 5th and 6th at Old Fashion Baptist!
Come join us for Ranger Walkers Wild West Cowboy Carnival!
</p>
<br>
<p ofbFadeInOnScroll>
<b>Wednesday, September 5 @ 7 PM</b>
<br>
Bible Preaching & Bluegrass Gospel Music
<p ofbFadeInOnScroll class="bold">
Family Fun
</p>
<br>
<p ofbFadeInOnScroll>
<b>Thursday, September 6 @ 5:30 PM</b>
<br>
Food, Games, Music & Preaching
<p ofbFadeInOnScroll class="bold">
Games
</p>
<br>
<p ofbFadeInOnScroll>
<a href="assets/images/champ_flyer.jpg" download="champ_flyer.jpg"><b>Click here to download the flyer</b></a>
<p ofbFadeInOnScroll class="bold">
Rides
</p>
<br>
<p ofbFadeInOnScroll class="bold">
Inflatables
</p>
<br>
<p ofbFadeInOnScroll style="font-size:1.25em;">
<b>Sunday, September 18 @ 11:00 AM</b>
</p>
<br>
<p ofbFadeInOnScroll class="action">
@ -45,7 +119,7 @@
<div class="row">
<div class="row-content">
<div class="row-content-col-left align-top">
<div class="row-content-col-right align-top">
<p ofbFadeInOnScroll class="row-content-header">...that ye also may have fellowship with us...</p>
<p ofbFadeInOnScroll>It is exciting to gather together in the name of the Lord and
we sincerily hope that YOU will join us. Guests are always welcome at Old Fashion
@ -53,7 +127,7 @@
<p class="verse" ofbFadeInOnScroll>For where two or three are gathered together in my name, there am I in the midst of them. - Matthew 18:20</p>
<p ofbFadeInOnScroll class="action"><i ofbicon>info_outline</i> <a href="#" routerLink="/whoweare" class="align-top">Learn more about us</a></p>
</div>
<div class="row-content-col-right">
<div class="row-content-col-left">
<!-- <img ofbFadeInOnScroll src="assets/images/home-images/2-church-color.png"> -->
<a routerLink="/whoweare"><img ofbFadeInOnScroll src="assets/images/home-images/tiny/family.jpg"></a>
</div>

View File

@ -2,6 +2,10 @@ import { Component, HostListener } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material';
import { VideoPopupComponent } from '../popups/video-popup/video-popup.component';
import { environment } from '../../../environments/environment';
import { Countdown } from './countdown';
import { HttpClient } from '@angular/common/http';
import { VideoServices } from './video-services';
import { YoutubePopupComponent } from '../popups/youtube-popup/youtube-popup.component';
@Component({
selector: 'home-component',
@ -10,20 +14,55 @@ import { environment } from '../../../environments/environment';
})
export class HomeComponent {
backgroundTop: string = "0px";
public get showVGC() : boolean {
let maxDate = new Date(2024,7,15,11); // August 15th 2018 -- Set the month one month behind since JavaScript dates are 0 based
let now = new Date();
if (now.getFullYear() > maxDate.getFullYear()) return false;
if (now.getFullYear() == maxDate.getFullYear()) {
if (now.getMonth() > maxDate.getMonth()) return false;
if (now.getMonth() == maxDate.getMonth()) {
if (now.getDate() > maxDate.getDate()) return false;
if (now.getDate() == maxDate.getDate()) {
if (now.getHours() > maxDate.getHours()) return false;
}
}
}
return true;
}
public get showSpecial() : boolean {
let maxDate = new Date(2018,8,6); // September 6th 2018 -- Set the month one month behind since JavaScript dates are 0 based
let maxDate = new Date(2022,8,18,11); // September 18th 2018 -- Set the month one month behind since JavaScript dates are 0 based
let now = new Date();
if (now.getFullYear() > maxDate.getFullYear()) return false;
if (now.getFullYear() == maxDate.getFullYear()) {
if (now.getMonth() > maxDate.getMonth()) return false;
if (now.getMonth() == maxDate.getMonth()) {
if (now.getDate() > maxDate.getDate()) return false;
if (now.getDate() == maxDate.getDate()) {
if (now.getHours() > maxDate.getHours()) return false;
}
}
}
return true;
}
public get showCallToAction(): boolean {
let maxDate = new Date(2019,6,8); // September 6th 2018 -- Set the month one month behind since JavaScript dates are 0 based
let maxDate = new Date(2020,4,10); // July 8th 2018 -- Set the month one month behind since JavaScript dates are 0 based
let now = new Date();
if (now.getFullYear() > maxDate.getFullYear()) return false;
if (now.getFullYear() == maxDate.getFullYear()) {
if (now.getMonth() > maxDate.getMonth()) return false;
if (now.getMonth() == maxDate.getMonth()) {
if (now.getDate() > maxDate.getDate()) return false;
}
}
return true;
}
public get showVBS(): boolean {
let maxDate = new Date(2019,7,22); // August 22nd 2018 -- Set the month one month behind since JavaScript dates are 0 based
let now = new Date();
if (now.getFullYear() > maxDate.getFullYear()) return false;
if (now.getFullYear() == maxDate.getFullYear()) {
@ -35,11 +74,26 @@ export class HomeComponent {
return true;
}
constructor(private dialog: MatDialog)
{
public countdown: Countdown;
public showCountdown: boolean = false;
constructor(private dialog: MatDialog, private http: HttpClient)
{
this.http.get<VideoServices>('assets/json/videoServices.json').subscribe(res => {
this.countdown = new Countdown(res.videos);
});
}
playVictoryGospelCrusade() {
const url = 'https://drive.google.com/file/d/1gPdCTLkJEtNWEyn_y8FQ78PaDi40MU7N/preview'
let opts = new MatDialogConfig;
opts.data = { title: 'Victory Gospel Crusade', embedUrl: url };
let dialog = this.dialog.open(YoutubePopupComponent, opts);
}
@HostListener('window:scroll', ['$event'])
onScroll(event){
let scrollTop = event.target.documentElement.scrollTop || event.target.body.scrollTop || window.pageYOffset;

View File

@ -0,0 +1,12 @@
export class VideoServices {
videos: VideoService[];
}
export class VideoService {
isReady: boolean;
title: string;
src: string;
date: Date;
archived: boolean;
yt: string;
}

View File

@ -0,0 +1,16 @@
.fab-buttons{
display: none;
}
.side-bar{
position: fixed;
}
@media(max-width:800px){
.fab-buttons{
display: inline-block;
position: fixed;
bottom: 20px;
right: 20px;
}
}

View File

@ -0,0 +1,13 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent class="mapWrapper">
<img style="width: 100%" src="{{imageUrl}}">
<div class="fab-buttons" >
<button mat-fab (click)="share()"><i ofbicon>share</i></button>
</div>
</div>
<div sideBar ofbFadeInOnScroll>
<div class="side-bar">
<button style="width: 100%;" mat-stroked-button (click)="share()"><i ofbicon>share</i> Share</button>
</div>
</div>
</secondary-page-component>

View File

@ -0,0 +1,40 @@
import { BibleVerseService } from './../../services/bible-verse.service';
import { Component, OnInit, Inject, Input } from '@angular/core';
import { MatDialogConfig, MatDialog } from '@angular/material';
import { SharePopupComponent } from '../popups/share-popup/share-popup.component';
import { DOCUMENT } from '@angular/platform-browser';
import { Router, ActivatedRouteSnapshot, ActivatedRoute } from '@angular/router';
@Component({
selector: 'image-component',
templateUrl: './image.component.html',
styleUrls: ['./image.component.css']
})
export class ImageComponent implements OnInit {
public imageUrl: string;
public shareName: string
constructor(private dialog: MatDialog, @Inject(DOCUMENT) private document, private route: ActivatedRoute) {
this.imageUrl = this.route.snapshot.data.imageUrl;
this.shareName = this.route.snapshot.data.shareName;
}
ngOnInit() {
// this.bibleVerseService.randomVerse().subscribe(
// verse => {
// },
// error => console.log(error) );
}
public share() {
let opts = new MatDialogConfig;
opts.data = {
prefix: 'o',
otherName: this.shareName
};
let dialog = this.dialog.open(SharePopupComponent, opts);
}
}

View File

@ -0,0 +1,17 @@
.video-container {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%;
}
.video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.opacity-zero {
opacity: 0;
}

View File

@ -0,0 +1,19 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent class="mapWrapper">
<h2 *ngIf="!showVideo">{{message}}</h2>
<div class="video-container" *ngIf="showVideo && !yt">
<video [class.opacity-zero]="!showVideo" *ngIf="!error" style="width:100%" [src]="videoSrc" controls [poster]="poster" (error)="vidError()"></video>
</div>
<div *ngIf="showVideo && yt" class="video-container">
<iframe [src]="yt | safeUrl" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div style="margin-top: 10px">
<button routerLink="/video" style="margin-top:5px; width:100%;" mat-stroked-button>
<b><i ofbicon style="margin-top:-4px; vertical-align: middle;">play_arrow</i> Click here for previous video services</b>
</button>
</div>
</div>
<div sideBar ofbFadeInOnScroll>
</div>
</secondary-page-component>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LiveStreamComponent } from './live-stream.component';
describe('LiveStreamComponent', () => {
let component: LiveStreamComponent;
let fixture: ComponentFixture<LiveStreamComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LiveStreamComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LiveStreamComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,66 @@
import { BibleVerseService } from './../../services/bible-verse.service';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { VideoServices } from '../home/video-services';
import { DateTime } from "luxon";
@Component({
selector: 'live-stream-component',
templateUrl: './live-stream.component.html',
styleUrls: ['./live-stream.component.css']
})
export class LiveStreamComponent implements OnInit {
public sermonVideo: string = 'https://ofbbutte.com/static/media/video/sermon.mp4';
public pleaseWait: string = 'https://ofbbutte.com/static/media/video/PleaseWait.jpg';
public clickToPlay: string = 'https://ofbbutte.com/static/media/video/ClickToPlay.jpg';
public message: string = "Please Wait...";
public isReady: boolean = false;
public videoSrc: string = this.sermonVideo;
public yt: string;
public poster: string = this.clickToPlay;
public error: boolean = false;
public showVideo: boolean = false;
constructor(private http: HttpClient) { }
private interval;
ngOnInit() {
this.update();
this.interval = setInterval(() => {
this.update();
}, 3000);
}
update() {
this.http.get<VideoServices>('assets/json/videoServices.json').subscribe(res => {
res.videos = res.videos.filter(v => v.archived === false);
res.videos.forEach(v => v.date = DateTime.fromISO(v.date, {zone: 'America/Denver'}));
res.videos.sort((a, b) => {
if (a.date < b.date) return -1;
if (a.date === b.date) return 0;
return 1;
});
var service = res.videos[0];
if (service) {
this.message = (<any>service).message;
if (service.isReady === true) {
this.showVideo = true;
this.videoSrc = '';
this.videoSrc = service.src;
this.yt = service.yt;
clearInterval(this.interval);
}
} else {
this.message = 'No Live Services';
}
});
}
}

View File

@ -0,0 +1,58 @@
.w-50 {
width: 50%;
}
.w-100 {
width: 100%;
}
.section-header{
font-size: 1.2em;
font-weight: bold;
border-bottom: 1px solid gray;
margin-bottom: 10px;
}
.error {
color: red;
}
mat-label {
font-weight: bold;
overflow-wrap: break-word;
}
label {
display: flex;
flex-direction: column;
font-size: 1.15rem;
margin-top: 35px;
color: black;
font-weight: 400;
font-family: Arial, Helvetica, sans-serif
}
ol > li {
margin-left: 20px;
}
mat-label {
font-size: 1.15rem;
color: black;
font-weight: 400;
}
label.sub {
margin-top: 0px;
font-size: .85rem;
}
mat-radio-button {
display: flex;
flex-direction: column;
margin: 15px 0;
}
.hidden {
display: none;
}

View File

@ -0,0 +1,326 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent>
<p class="section-header">
Old Fashion Baptist Church Missionary Questionnaire
</p>
<p>
I hate questionnaires! I've been there. I understand that deputation itself is tough enough already without some pastor probing into all kinds of areas! Then there is the fact that so many fundamental pastors themselves disagree in so many areas!
<br><br>
Now that Ive said what you may have already been thinking, let me also say that I am continually amazed that every church doesnt use a questionnaire to vet their missionaries. The truth is, we take seriously the fact that you are being considered for support by our church and feel that we cant truly get to know you unless we ask some simple questions.
<br><br>
You must understand that anyone whom we consider as a missionary for our Church has certain doctrinal beliefs which are assumed by us or else we would not be sending them a questionnaire. We also realize that good men will differ on some things and will make allowances accordingly. Also, because we have one questionnaire for all candidates, many of the things that are asked would be unnecessary if it were not being sent to everyone. We hope you understand that. In order for a missionary to be considered, we request that every question be answered.
<br><br>
Thank you for your time and understanding. The questions are not meant in any way to be “trick” questions.
<br><br>
God bless you!
<br><br>
Pastor Derek Loewen
</p>
<br><br>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<mat-form-field class="w-50" [class.background-error]="hasErrors('name')">
<mat-label>Name</mat-label>
<input matInput type="text" formControlName="name" required>
</mat-form-field>
<mat-form-field class="w-50">
<mat-label>Wifes Name</mat-label>
<input matInput type="text" formControlName="wifesName" >
</mat-form-field>
<mat-form-field class="w-100">
<mat-label>Home Phone</mat-label>
<input matInput type="tel" formControlName="homePhone" >
</mat-form-field>
<mat-form-field class="w-100">
<mat-label>Cell Phone</mat-label>
<input matInput type="tel" formControlName="cellPhone" >
</mat-form-field>
<mat-form-field class="w-100">
<mat-label>Field Phone</mat-label>
<input matInput type="tel" formControlName="fieldPhone" >
</mat-form-field>
<mat-form-field class="w-100">
<mat-label>Number of Children</mat-label>
<input matInput type="number" formControlName="numberOfChildren" (keyup)="numberOfChildrenChange($event.target.value)" required >
</mat-form-field>
<div *ngIf="form.get('numberOfChildren').value > 0">
<label id="childrenNamesLabel" >Names of Children</label>
<ol>
<li formArrayName="children" *ngFor="let child of form.get('children').controls; let i = index;">
<div [formGroupName]="i">
<mat-form-field floatLabel="never" class="w-100">
<mat-label>Childs Name</mat-label>
<input matInput type="text" formControlName="name" >
</mat-form-field>
</div>
</li>
</ol>
</div>
<label id="testimonyLabel">Please give your testimony</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="testimony" aria-labelledby="testimonyLabel" required></textarea>
</mat-form-field>
<label id="callToFieldLabel">Please explain specifically your call to the field.</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="callToField" required></textarea>
</mat-form-field>
<label id="sendingChurchLabel">Sending Church or Missions Board:</label>
<mat-form-field class="w-100" floatLabel="never">
<input matInput type="text" placeholder="Your Answer" formControlName="sendingChurch" required>
</mat-form-field>
<label id="fieldOfServiceLabel">What is your planned field of Service?</label>
<label class="sub">Country and City</label>
<mat-form-field class="w-100" floatLabel="never">
<input matInput type="text" placeholder="Your Answer" formControlName="fieldOfService" required>
</mat-form-field>
<label id="plansLabel">What do your plans include?</label>
<label class="sub">(Starting a church, continuing a church, starting a school, evangelizing the area...)</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="plans" required></textarea>
</mat-form-field>
<label id="evaluationOfNationalsLabel">What is your evaluation of the nationals you are trying to reach, spiritually and culturally?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="evaluationOfNationals" required></textarea>
</mat-form-field>
<label id="timeInCountryLabel">How long have you been in this country?</label>
<label class="sub">If you have changed countries, what other countries have you been in and for how long?</label>
<mat-form-field class="w-100" floatLabel="never">
<input matInput type="text" placeholder="Your Answer" formControlName="timeInCountry" required>
</mat-form-field>
<label id="correctWrongOfAnotherMissionaryLabel">If you knew that a fellow missionary was doing wrong or something quesstionable, what would be your course of action?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="correctWrongOfAnotherMissionary" required></textarea>
</mat-form-field>
<label id="financialStatementPrevYearLabel">Would you submit a general financial statement for the past year?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="financialStatementPrevYear" required></textarea>
</mat-form-field>
<label id="currentMonthlySupportLabel">What is your total monthly support (peronal, work fund, etc)?</label>
<mat-form-field class="w-100" floatLabel="never">
<input matInput type="text" placeholder="Your Answer" formControlName="currentMonthlySupport" required>
</mat-form-field>
<label id="monthlySupportNeededLabel">How much monthly support do you calculate you will need?</label>
<label class="sub">(We know that there will be differences)</label>
<mat-form-field class="w-100" floatLabel="never">
<input matInput type="text" placeholder="Your Answer" formControlName="monthlySupportNeeded" required>
</mat-form-field>
<label id="restAndRelaxationLabel">Are you diligent to have "Rest and Relaxation" during your stay on the field? Do you have a set time and plan for such necessary refreshing?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="restAndRelaxation" required></textarea>
</mat-form-field>
<label id="aloneOrTeamLabel">Do you plan to work alone or as a team? What are your feelings about the team concept?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="aloneOrTeam" required></textarea>
</mat-form-field>
<label id="childrenSchoolLabel">Do your children:</label>
<label class="sub">Please mark even if your children are yet to be born.</label>
<mat-radio-group aria-label="Type of School" formControlName="childrenSchool" aria-labelledby="childrenSchoolLabel">
<mat-radio-button class="radio-button" value="1">Attend a Christian School</mat-radio-button>
<mat-radio-button class="radio-button" value="2">Attend a Public School</mat-radio-button>
<mat-radio-button class="radio-button" value="3">Home School</mat-radio-button>
<mat-radio-button class="radio-button" value="4">Other</mat-radio-button>
</mat-radio-group>
<label id="danceLabel">Do you or any of your family ever dance?</label>
<mat-radio-group aria-label="Dance" formControlName="dance" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="worldlyMusicLabel">Do you or any of your family ever listen to worldly music in your home?</label>
<label class="sub">(Including but not limited to: rock, country/western, r&b, etc.)</label>
<mat-radio-group aria-label="Worldy Music" formControlName="worldlyMusic" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="movieTheatersLabel">Do you or any of your family ever attend public movie theaters?</label>
<mat-radio-group aria-label="Movie Theaters" formControlName="movieTheaters" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="alcoholLabel">Do you or any of your family ever use any type of alcoholic beverage?</label>
<mat-radio-group aria-label="Alcohol" formControlName="alcohol" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="smokingLabel">Do you or any of your family ever engage in any unclean habit such as smoking?</label>
<mat-radio-group aria-label="Smoking" formControlName="smoking" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="maleHairLabel">If there are boys in your home, do they follow the same hair standard you follow?</label>
<mat-radio-group aria-label="Male Hair" formControlName="maleHair" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="femalSlacksLabel">Does your wife wear slacks in public?</label>
<mat-radio-group aria-label="Female Slacks" formControlName="femaleSlacks" class="w-100">
<mat-radio-button value="1">Rarely</mat-radio-button>
<mat-radio-button value="2">Often</mat-radio-button>
<mat-radio-button value="3">On Sports Activities</mat-radio-button>
<mat-radio-button value="4">Never</mat-radio-button>
</mat-radio-group>
<label id="femaleShortsLabel">Does your wife wear shorts in public?</label>
<mat-radio-group aria-label="Female Shorts" formControlName="femaleShorts" class="w-100">
<mat-radio-button value="1">Rarely</mat-radio-button>
<mat-radio-button value="2">Often</mat-radio-button>
<mat-radio-button value="3">On Sports Activities</mat-radio-button>
<mat-radio-button value="4">Never</mat-radio-button>
</mat-radio-group>
<label id="femaleDressStandardLabel">Do the girls in your family follow the same dress standard your wife does?</label>
<mat-radio-group aria-label="Female Dress Standard" formControlName="femaleDressStandard" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="swimmingClothingLabel">What do you do for clothing when you go swimming?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="swimmingClothing" required></textarea>
</mat-form-field>
<label id="televisionLabel">How do you feel about having a television in the home?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="television" required></textarea>
</mat-form-field>
<label id="dailyBibleLabel">Do you MAKE time daily for Bible Reading and Prayer?</label>
<mat-radio-group aria-labeled-by="dailyBibleLabel" formControlName="dailyBible" class="w-100">
<mat-radio-button value="1">Always</mat-radio-button>
<mat-radio-button value="2">Sometimes</mat-radio-button>
<mat-radio-button value="3">Rarely</mat-radio-button>
</mat-radio-group>
<label id="numberLedToChrist">Outside of the church services where you have ministered, estimate how many people you have personally won to Christ in the last six months.</label>
<mat-form-field class="w-100" floatLabel="never">
<input matInput type="number" placeholder="Your Answer" formControlName="numberLedToChrist" required>
</mat-form-field>
<label id="numberWitnessedToLabel">How many people have you personally witnessed to?</label>
<mat-form-field class="w-100" floatLabel="never">
<input matInput type="number" placeholder="Your Answer" formControlName="numberWitnessedTo" required>
</mat-form-field>
<label id="numberWeeklyTractsLabel">On average, how many tracts do you pass out in a week?</label>
<mat-form-field class="w-100" floatLabel="never">
<input matInput type="number" placeholder="Your Answer" formControlName="numberWeeklyTracts" required>
</mat-form-field>
<label id="rateOfSuccessLabel">What is your rate of success in fulfilling the great commission, getting your converts baptized and active in the local church where they may be taught?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="rateOfSuccess" required></textarea>
</mat-form-field>
<label id="predestinationLabel">Explain your position briefly concerning predestination and election.</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="predestination" required></textarea>
</mat-form-field>
<label id="fellowshipAssociationLabel">Do you claim identity with any group or fellowship such as BBF, GARB, FBF, Other?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="fellowshipAssociation" required></textarea>
</mat-form-field>
<label id="collegeRecommendationsLabel">To a young person asking you to recommend a Christian College, list two or three Christian Colleges, in order of preference, which you would recommend?</label>
<label class="sub">Please list the name, city, and state for each</label>
<div *ngIf="form.get('collegeRecommendations').errors?.minColleges && (form.touched || form.dirty)" class="error">
{{form.get('collegeRecommendations').errors?.minColleges}}
</div>
<ol>
<li formArrayName="collegeRecommendations" *ngFor="let college of collegeRecommendationFormArray().controls; let i = index;">
<div [formGroupName]="i">
<mat-form-field floatLabel="never" class="w-100">
<mat-label>Name</mat-label>
<input matInput type="text" formControlName="name">
</mat-form-field>
<mat-form-field floatLabel="never" class="w-50">
<mat-label>City</mat-label>
<input matInput type="text" formControlName="city">
</mat-form-field>
<mat-form-field floatLabel="never" class="w-50">
<mat-label>State</mat-label>
<input matInput type="text" formControlName="state">
</mat-form-field>
</div>
<div *ngIf="college.errors?.college && (form.touched || form.dirty)" class="error">
{{college.errors?.college}}
</div>
</li>
</ol>
<label id="admittedWrongLabel">When was the last time you admitted you were wrong about something and apologized?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="admittedWrong" required></textarea>
</mat-form-field>
<label id="divorcedLabel">Have you or your wife ever been divorced?</label>
<mat-radio-group formControlName="divorced" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="groundsForRemarryLabel">Do you believe there are grounds whereby a man or women may remarry with a previous spouse still living?</label>
<mat-radio-group aria-label="Grounds for Remarry" formControlName="groundsForRemarry" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="marryADivorceeLabel">Have you married a divorced couple or would you?</label>
<mat-radio-group aria-label="Married a Divorcee" formControlName="marryADivorcee" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="masonicLodge">Do you believe that being a member of a masonic lodge conflicts with Biblical separation?</label>
<mat-radio-group aria-label="Masonic Lodge" formControlName="masonicLodge" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="bibleVersionsUsedLabel">Please list the English version(s) of the Bible you use and approve for preaching and teaching.</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="bibleVersionsUsed" required></textarea>
</mat-form-field>
<label id="bibleVersionOpinionLabel">Please give your opinion of all English versions of the Bible you use and approve for preaching and teaching.</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="bibleVersionOpinion" required></textarea>
</mat-form-field>
<label id="contemporaryMusicLabel">Do you listen to or approve of either "Contemporary Christian Music" or "Gospel rock" music?</label>
<mat-radio-group formControlName="contemporaryMusic" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="charasmaticismLabel">What is your position in relation to the modern day Charismatic movement?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="charasmaticism" required></textarea>
</mat-form-field>
<label id="tonguesLabel">Do you or any member of your family speak in tongues?</label>
<mat-radio-group formControlName="tongues" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="repentanceNecessaryLabel">Is Repentance a necessary part of salvation?</label>
<mat-radio-group formControlName="repentanceNecessary" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="repentanceDefinitionLabel">One definition of repentance is: "Repentance means changing your mind about whatever is keeping you from turning to Jesus."</label>
<label class="sub">Do you agree or disagree? Explain your answer.</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="repentanceDefinition" required ></textarea>
</mat-form-field>
<label id="fundamentalistLabel">Do you consider yourself a fundamentalist?</label>
<mat-radio-group formControlName="fundamentalist" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="billsOnTimeLabel">Do you believe it is important to pay your bills on time?</label>
<mat-radio-group formControlName="billsOnTime" class="w-100">
<mat-radio-button value="true">Yes</mat-radio-button>
<mat-radio-button value="false">No</mat-radio-button>
</mat-radio-group>
<label id="lateBillsLabel">How many times in the last 12 months have you been late paying a bill?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="lateBills" required></textarea>
</mat-form-field>
<label id="lateBillActionTakenLabel">What do you do to "make things right" when you are late paying a bill?</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="lateBillActionTaken" required></textarea>
</mat-form-field>
<label id="potentialHarvestLabel">Though we support Missions and Missionaries doing many things and serving on many fields, we look primarily for Missionaries going to the ripe Harvest fields to begin Churches which will eventually become indigenous Churches. Please evaluate for us the potential of the Harvest field to which you have been called by telling the response of the people at large to Tract or Bible Distribution, Door-to-Door Visitation, and Gospel Preaching.</label>
<mat-form-field class="w-100" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="potentialHarvest" required></textarea>
</mat-form-field>
<mat-form-field class="w-100 hidden" floatLabel="never">
<textarea matInput type="text" placeholder="Your Answer" formControlName="honeyPot" required></textarea>
</mat-form-field>
<button mat-raised-button type="submit" [disabled]="!form.valid || submitButtonDisabled">{{submitButtonText}}</button>
<br>
<p *ngIf="!form.valid">Please complete all fields to submit</p>
</form>
</div>
<div sideBar class="side-bar">
</div>
</secondary-page-component>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MissionaryFormPageComponent } from './missionary-form-page.component';
describe('MissionaryFormPageComponent', () => {
let component: MissionaryFormPageComponent;
let fixture: ComponentFixture<MissionaryFormPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ MissionaryFormPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MissionaryFormPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,184 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormArray, FormControl, ValidatorFn, ValidationErrors } from '@angular/forms';
import { MissionarySupportService } from 'src/app/services/missionary-support-service';
import { MatDialog, MatDialogConfig } from '@angular/material';
import { OkPopupComponent } from '../popups/ok-popup/ok-popup.component';
@Component({
selector: 'app-missionary-form-page',
templateUrl: './missionary-form-page.component.html',
styleUrls: ['./missionary-form-page.component.css']
})
export class MissionaryFormPageComponent implements OnInit {
form: FormGroup;
submitButtonText: string = 'Submit';
submitButtonDisabled: boolean = false;
errorMessages: string[] = [];
private maxNumberOfChildren: number = 30;
constructor(private formBuilder: FormBuilder, private matDialog: MatDialog, private missionarySupportService: MissionarySupportService) {
this.setupForm();
}
ngOnInit() {
}
setupForm() {
this.form = this.formBuilder.group({
name: ['', [Validators.required]],
homePhone: [''],
cellPhone: [''],
fieldPhone: [''],
wifesName: [''],
numberOfChildren: ['', [Validators.required, Validators.max(this.maxNumberOfChildren)]],
children: this.formBuilder.array([]),
testimony: ['', Validators.required],
callToField: ['', Validators.required],
sendingChurch: ['', Validators.required],
fieldOfService: ['', Validators.required], //Country + City
plans: ['', Validators.required],
evaluationOfNationals: ['', Validators.required],
timeInCountry: ['', Validators.required],
correctWrongOfAnotherMissionary: ['', Validators.required],
financialStatementPrevYear: [undefined, Validators.required],
currentMonthlySupport: ['', Validators.required],
monthlySupportNeeded: ['', Validators.required],
restAndRelaxation: ['', Validators.required],
aloneOrTeam: ['', Validators.required],
childrenSchool: [undefined, Validators.required],
dance: ['', Validators.required],
worldlyMusic: [undefined, Validators.required],
movieTheaters: [undefined, Validators.required],
alcohol: [undefined, Validators.required],
smoking: [undefined, Validators.required],
maleHair: [undefined, Validators.required],
femaleSlacks: [undefined, Validators.required],
femaleShorts: [undefined, Validators.required],
femaleDressStandard: [undefined, Validators.required],
swimmingClothing: [undefined, Validators.required],
television: [undefined, Validators.required],
dailyBible: [undefined, Validators.required],
numberLedToChrist: ['', [Validators.required, Validators.min(0), Validators.max(10000)]],
numberWitnessedTo: ['', [Validators.required, Validators.min(0), Validators.max(10000)]],
numberWeeklyTracts: ['', [Validators.required, Validators.min(0), Validators.max(10000)]],
rateOfSuccess: ['', Validators.required],
predestination: ['', Validators.required],
fellowshipAssociation: ['', Validators.required],
collegeRecommendations: this.formBuilder.array([this.createCollegeRecommendation(),this.createCollegeRecommendation(),this.createCollegeRecommendation()], this.minCollegeValidator),
admittedWrong: ['', Validators.required],
divorced: [undefined, Validators.required],
groundsForRemarry: [undefined, Validators.required],
marryADivorcee: [undefined, Validators.required],
masonicLodge: [undefined, Validators.required],
bibleVersionsUsed: ['', Validators.required],
bibleVersionOpinion: ['', Validators.required],
contemporaryMusic: ['', Validators.required],
charasmaticism: ['', Validators.required],
tongues: [undefined, Validators.required],
repentanceNecessary: [undefined, Validators.required],
repentanceDefinition: [undefined, Validators.required],
fundamentalist: [undefined, Validators.required],
billsOnTime: [undefined, Validators.required],
lateBills: ['', Validators.required],
lateBillActionTaken: ['', Validators.required],
potentialHarvest: ['', Validators.required],
honeyPot: ['.', Validators.required]
});
}
collegeRecommendationFormArray(): FormArray {
return this.form.get('collegeRecommendations') as FormArray;
}
addChildToForm() {
const children = this.form.get('children') as FormArray;
children.push(this.formBuilder.group({
name: ['', Validators.required]
}));
}
createCollegeRecommendation(): FormGroup {
return this.formBuilder.group({
name: [''],
city: [''],
state: ['']
}, { validators: [this.collegeValidator]});
}
numberOfChildrenChange(number: Number) {
let num = +number;
num = num > this.maxNumberOfChildren ? this.maxNumberOfChildren : num;
const children = this.form.get('children') as FormArray;
while (children.length > num) {
children.removeAt(children.length - 1);
}
while (children.length < num) {
children.push(this.formBuilder.group({
name: ['', Validators.required]
}));
}
}
hasErrors(formControlName: string): boolean {
let res = this.form.get(formControlName) && typeof this.form.get(formControlName).errors !== 'undefined';
if (res === true) {
res = this.form.get(formControlName).dirty || (this.form.get(formControlName).touched && this.form.get(formControlName).valid === false);
}
return res;
}
onSubmit(){
this.errorMessages = [];
if (this.errorMessages.length > 0){ return; }
this.submitButtonText = "Please Wait...";
this.submitButtonDisabled = true;
this.missionarySupportService.create(this.form.value)
.subscribe(
success => {this.emailSuccess();},
error => {this.emailError();});
}
private emailSuccess(){
let opts = new MatDialogConfig;
opts.data = { title:'Missionary Support','message':'Thank You! Your submission has been received.' };
let popup = this.matDialog.open(OkPopupComponent,opts);
popup.afterClosed().subscribe(()=>{
this.submitButtonText = "Form Submitted"
});
}
private emailError(){
console.error("error");
let opts = new MatDialogConfig;
opts.data = { title:'Error','message':'Please make sure that you have entered all fields correctly.' };
let popup = this.matDialog.open(OkPopupComponent,opts);
this.submitButtonText = "Submit";
this.submitButtonDisabled = false;
}
private minCollegeValidator(control: FormArray): ValidationErrors | null {
const minRequired = 2;
let valid = 0;
for(let i = 0; i < control.controls.length; i++) {
const val = control.controls[i].value;
if (val.name && val.city && val.state && val.name.length > 0 && val.city.length > 0 && val.state.length > 0) {
valid++;
}
}
return valid >= minRequired ? null : { 'minColleges': 'Please enter at least two colleges.' };
}
private collegeValidator(control: FormGroup): ValidationErrors | null {
const name = control.value.name || '';
const city = control.value.city || '';
const state = control.value.state || '';
if (name.length > 0 || city.length > 0 || state.length > 0) {
if (name.length === 0 || city.length === 0 || state.length === 0) {
return { 'college': 'Please enter name, city and state for each college.' };
}
}
return null;
}
}

View File

@ -88,7 +88,8 @@ export class AddSermonPopupComponent implements OnInit {
if (this.sermonFile == null){
this.errorMessages.push("Please add a sermon MP3 file");
}
if (this.sermonFile != null && this.sermonFile.type != 'audio/mp3'){
if (this.sermonFile != null && this.sermonFile.type != 'audio/mpeg'){
this.errorMessages.push("File must be a MP3");
}
if (this.errorMessages.length > 0){ this.updateAddButton(true); return; }

View File

@ -1,3 +1,7 @@
.full-width {
width: 100%;
}
.m-5 {
margin: 5px;
}

View File

@ -31,7 +31,7 @@
{{error}}
</p>
</div>
<button mat-raised-button class="first" (click)="cancel($event)" >Cancel</button><!--
--><button mat-raised-button type="submit" [disabled]="!addUserForm.form.valid || addUserButtonDisabled">{{ addUserButtonText }}</button>
<button mat-raised-button class="first m-5" (click)="cancel($event)" >Cancel</button><!--
--><button mat-raised-button class="m-5" type="submit" [disabled]="!addUserForm.form.valid || addUserButtonDisabled">{{ addUserButtonText }}</button>
</form>
</div>

View File

@ -30,6 +30,12 @@ export class SharePopupComponent implements OnInit {
this.id = data.id;
let port = this.document.location.port ? ":"+this.document.location.port : "";
this.shareUrl = this.document.location.protocol +'//'+ this.document.location.hostname + port + this.shareBaseUrl + data.prefix + this.id;
if (this.data.prefix === 'o') {
this.shareUrl = this.document.location.protocol + '//' + this.document.location.hostname + port + this.shareBaseUrl + data.prefix + data.otherName;
}
if (this.data.url) {
this.shareUrl = this.data.url;
}
this.facebookIframeUrl = this.urlPartA + this.shareUrl + this.urlPartB;
this.twitterUrl = this.twitterPartA + data.title + " - " + data.description + "&url=" + this.shareUrl;
}

View File

@ -0,0 +1,26 @@
iframe {
max-width: 100%;
}
h2 {
display: inline-block;
margin-bottom: 15px;
width: calc(100% - 50px);
}
hr{
margin-bottom: 15px;
}
i {
font-size: 24pt;
}
.vertical-align-top {
vertical-align: top;
}
.align-right {
text-align: right;
}

View File

@ -0,0 +1,10 @@
<div md-dialog-title>
<h2>{{title}}</h2>
<button mat-icon-button (click)="close()">
<i ofbicon>close</i>
</button>
</div>
<hr color="primary">
<div md-dialog-content>
<iframe width="560" height="315" [src]="embedUrl | safeUrl" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { YoutubePopupComponent } from './youtube-popup.component';
describe('YoutubePopupComponent', () => {
let component: YoutubePopupComponent;
let fixture: ComponentFixture<YoutubePopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ YoutubePopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(YoutubePopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,26 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
@Component({
selector: 'app-youtube-popup',
templateUrl: './youtube-popup.component.html',
styleUrls: ['./youtube-popup.component.css']
})
export class YoutubePopupComponent implements OnInit {
public title: string;
public embedUrl: string;
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private MatDialogRef: MatDialogRef<YoutubePopupComponent>) {
this.title = data.title;
this.embedUrl = data.embedUrl;
}
ngOnInit() {
}
public close() {
this.MatDialogRef.close();
}
}

View File

@ -8,7 +8,7 @@
<div class="description">{{ description }}</div><!--
--><div class="action-buttons">
<button mat-button class="action pct40" (click)="play()" ><i ofbicon>headset</i> Listen</button><!--
--><button mat-button class="action pct50" (click)="download()" ><i ofbicon>file_download</i> Download</button><!--
--><a download="{{title}}" href="{{url}}" mat-button class="action pct50" ><i ofbicon>file_download</i> Download</a><!--
--><button mat-button class="action pct10" (click)="share()" ><i ofbicon>share</i> Share</button>
</div><!--
--><div class="action-buttons" *ngIf="loggedIn">

View File

@ -0,0 +1,92 @@
.wrapper{
box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
}
.photo{
box-sizing: border-box;
display: inline-block;
max-width: 180px;
margin: 0;
vertical-align: top;
}
.fade{
text-align: center;
vertical-align: bottom;
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 20px;
padding-bottom: 2px;
/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#ffffff+0,ffffff+100&0+0,0.8+100 */
background: -moz-linear-gradient(top, rgba(255,255,255,0) 0%, rgba(255,255,255,0.8) 100%); /* FF3.6-15 */
background: -webkit-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,0.8) 100%); /* Chrome10-25,Safari5.1-6 */
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%,rgba(255,255,255,0.8) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#ccffffff',GradientType=0 ); /* IE6-9 */
}
.photo button{
padding: 0;
vertical-align: top;
}
button img{
display: block;
height: 100%;
width: 100%;
}
.info{
display: inline-block;
vertical-align: top;
padding: 2px 2px 8px 5px;
box-sizing: border-box;
width: calc(100% - 180px);
overflow-y: hidden;
position: relative;
height: 100px;
}
.title{
font-weight: bold;
}
.date, .author{
font-style: italic;
font-size: 14px;
}
.description{
font-size: 14px;
color: darkgray;
word-wrap: none;
border-top: 0px solid lightgray;
padding-top:6px;
margin-top: 6px;
}
.expanded{
overflow-y: hidden;
}
.expanded-content{
margin: 10px;
}
.buttons, .buttons-edit{
background-color: lightgray;
}
.buttons button, .buttons .filler{
display: inline-block;
width: 50%;
}
.border{
margin: 0px 5px 0px 5px;
border-top: 1px inset lightgray;
}
.buttons-edit button{
display: inline-block;
width: 50%;
}

View File

@ -0,0 +1,26 @@
<div class="wrapper" [@inout]="startFadeIn">
<div class="photo">
<button mat-button (click)="play()">
<img src="{{thumbnailUrl}}" />
</button>
</div><!--
--><div class="info" (click)="toggleState()">
<span class="title">{{ title }}</span>
<br>
<span class="date">{{ publishTime | ofbDate:true }}</span>
<br>
<p class="description" [@toggleAnimationFade]="state">
{{ description }}
</p>
<div class="fade"><i ofbicon *ngIf="state === 'closed'">arrow_drop_down</i><i ofbicon *ngIf="state === 'open'">arrow_drop_up</i></div>
</div>
<div class="expanded" [@toggleAnimation]="state" (click)="toggleState()">
<p class="expanded-content">
{{ description }}
</p>
</div>
<div class="buttons">
<button mat-button class="action pct50" (click)="play()" ><i ofbicon>tv</i> Play</button><!--
--><button mat-button class="action pct50" (click)="share()" ><i ofbicon>share</i> Share</button>
</div>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { VideoItemComponent } from './video-item.component';
describe('VideoItemComponent', () => {
let component: VideoItemComponent;
let fixture: ComponentFixture<VideoItemComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ VideoItemComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(VideoItemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,88 @@
import { Component, AfterContentInit, Input, Inject } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material';
import { trigger, state, style, transition, animate } from '@angular/animations';
import { SharePopupComponent } from './../popups/share-popup/share-popup.component';
import { YesNoPopupComponent } from './../popups/yes-no-popup/yes-no-popup.component';
import { YoutubePopupComponent } from '../popups/youtube-popup/youtube-popup.component';
@Component({
selector: 'app-video-item',
templateUrl: './video-item.component.html',
styleUrls: ['./video-item.component.css'],
animations:[
trigger("inout",[
state("1", style({ opacity: '1' })),
state("0",style({ opacity: '0' })),
transition('* => 1',[
animate('500ms ease-in-out')
]),
transition(':enter',[
animate('0ms')
])
]),
trigger('toggleAnimation', [
state('open', style({
height: '*',
})),
state('closed', style({
height: '0px',
})),
transition('open <=> closed', animate('500ms ease-in-out'))
]),
trigger('toggleAnimationFade', [
state('open', style({
opacity: 0,
})),
state('closed', style({
opacity: 1,
})),
transition('open <=> closed', animate('500ms ease-in-out'))
])
]
})
export class VideoItemComponent implements AfterContentInit {
public state: string = 'closed';
@Input()
id: number;
@Input()
title: string;
@Input()
description: string;
@Input()
publishTime: Date;
@Input()
thumbnailUrl: string;
@Input()
shareUrl: string;
@Input()
embedUrl: string;
public startFadeIn: boolean = false;
@Input()
public delayFadeIn: number = 100;
constructor(private dialog: MatDialog){
}
ngAfterContentInit(): void{
setTimeout(() => this.startFadeIn = true, this.delayFadeIn);
}
toggleState(){
this.state = this.state === 'open' ? 'closed' : 'open'
}
share(){
let opts = new MatDialogConfig;
opts.data = { url: this.shareUrl, id: this.id, title: this.title, description: '' };
let dialog = this.dialog.open(SharePopupComponent, opts);
}
play(){
let opts = new MatDialogConfig;
opts.data = { title: this.title, embedUrl:this.embedUrl };
let dialog = this.dialog.open(YoutubePopupComponent, opts);
}
}

View File

@ -0,0 +1,13 @@
.yt-container {
position: relative;
padding-bottom: 56.25%; /* 16:9 */
height: 0;
}
.yt-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

View File

@ -0,0 +1,17 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent>
<div *ngFor="let video of services; let i = index;" style="margin-top: 10px;">
<button (click)="showVideo(i)" mat-stroked-button style="margin-bottom: 10px;">
<i ofbicon class="mr-10" >live_tv</i>
{{getTitle(i)}}
</button>
<div *ngIf="video.show && video.yt" class="yt-container">
<iframe [src]="video.yt | safeUrl" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<video style="width:100%" *ngIf="video.show && !video.yt" src="{{video.src}}" controls poster="https://ofbbutte.com/static/media/video/ClickToPlay.jpg"></video>
</div>
</div>
<div sideBar class="side-bar">
</div>
</secondary-page-component>

View File

@ -0,0 +1,41 @@
import { WindowRefService } from './../../services/window-ref.service';
import { Component, HostListener, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { VideoServices, VideoService } from '../home/video-services';
import { DateTime } from "luxon";
@Component({
selector: 'video-services-component',
templateUrl: './video-services.component.html',
styleUrls: ['./video-services.component.css'],
})
export class VideoServicesComponent implements OnInit {
public loading: boolean = true;
public services: VideoService[];
constructor(private http: HttpClient){ }
ngOnInit(){
this.http.get<VideoServices>('assets/json/videoServices.json')
.subscribe(res => {
this.services = res.videos.filter(v => v.isReady === true);
this.loading = false;
});
}
showVideo(index: number) {
this.services.forEach(s => {
(<any>s).show = false;
});
(<any>this.services[index]).show = true;
}
getTitle(index: number): string {
const title = this.services[index].title;
const dte = DateTime.fromISO(this.services[index].date, {zone: 'America/Denver'})
return title + ' | ' + dte.toLocaleString(DateTime.DATETIME_MED);
}
}

View File

@ -0,0 +1,13 @@
.side-bar{
position: fixed;
}
.width100{
width: 100%;
}
#showAllButton{
margin-top: 10px;
}

View File

@ -0,0 +1,12 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent>
<app-youtube-list></app-youtube-list>
</div>
<div sideBar class="side-bar">
</div>
</secondary-page-component>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { VideoComponent } from './video.component';
describe('VideoComponent', () => {
let component: VideoComponent;
let fixture: ComponentFixture<VideoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ VideoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(VideoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-video',
templateUrl: './video.component.html',
styleUrls: ['./video.component.css']
})
export class VideoComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -91,6 +91,6 @@ p.margin-bottom {
@media(max-width: 450px) {
.float-left {
width: 175px
width: 200px;
}
}

View File

@ -17,9 +17,10 @@
<img class="image shadow container-3-col" src="assets/images/home-images/tiny/church.jpg" alt="Church Image" />
</div>
<br><br>
<p ofbFadeInOnScroll class="section-header">Pastor Derek Loewen</p>
<div ofbFadeInOnScroll class="section-paragraph overflow-hidden">
<img class="float-left margin-right-10 border-radius-5" src="assets/images/tiny/pstr.jpg" alt="Pastor Photo" width="225px" />
<img class="float-left margin-right-10 border-radius-5" src="assets/images/tiny/pstr3.jpg" alt="Pastor Photo" width="350px" />
<p class="margin-bottom">
Pastor Derek Loewen heard the gospel for the first time when a friend invited him to
Florence Baptist Church in Florence, Montana. They rode the bus to Sunday School.
@ -29,10 +30,10 @@
</p>
<p class="margin-bottom">
During his senior year of high school, Pastor Loewen yielded his life to the Lord.
Upon graduation from Crown College of the Bible in 2003, he served in the church he
Upon graduation from Bible College in 2003, he served in the church he
was saved in as a young boy before marrying his bride, Rebecca in June 2004.
The Lord has graciously allowed them to serve Him together across the country both
assisting pastors and planting a church. God has blessed their home with 3 delightful
assisting pastors and planting a church. God has blessed their home with 5 delightful
children.
</p>
<p>

View File

@ -0,0 +1,39 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { YoutubeSearchResult } from './youtube-snippet';
import { tap } from 'rxjs/operators';
const apiKey = 'AIzaSyDYI1UzFyc-5UvWiN7BqkWm-8vXU6i5Au0';
const baseUrl = `https://www.googleapis.com/youtube/v3/search`;
const part = `snippet`;
const channelId = `UCplK-bimKe_E8k6Gka87IaQ`;
const maxResults = `20`;
const order = `date`;
const itemType = `video`;
@Injectable()
export class YoutubeListService {
constructor(private httpClient: HttpClient){}
private nextPageToken: string;
public getFirstPage() : Observable<YoutubeSearchResult> {
const url = this.getUrl(undefined);
return this.httpClient.get<YoutubeSearchResult>(url).pipe(tap(x => this.nextPageToken = x.nextPageToken));
}
public getNextPage() : Observable<YoutubeSearchResult> {
const url = this.getUrl(this.nextPageToken);
return this.httpClient.get<YoutubeSearchResult>(url).pipe(tap(x => this.nextPageToken = x.nextPageToken));
}
private getUrl(nextPageToken: string): string {
let url = `${baseUrl}?type=${itemType}&part=${part}&channelId=${channelId}&maxResults=${maxResults}&order=${order}&key=${apiKey}`;
if (nextPageToken) {
url = url + `&pageToken=${nextPageToken}`;
}
return url;
}
}

View File

@ -0,0 +1,24 @@
.width100 {
width: 100%;
}
.mt-20 {
margin-top: 20px;
}
ul{
list-style: none;
}
li{
margin: 40px 0px 40px 0px; /* top right bottom left */
}
li:first-child{
margin-top: 0px;
}
li:last-child{
margin-bottom: 0px;
}

View File

@ -0,0 +1,22 @@
<ul>
<li *ngFor="let item of items">
<app-video-item
[title]="item.snippet.title"
[description]="item.snippet.description"
[publishTime]="item.snippet.publishTime"
[thumbnailUrl]="item.snippet.thumbnails.medium.url"
shareUrl="https://youtu.be/{{item.id.videoId}}"
embedUrl="https://www.youtube.com/embed/{{item.id.videoId}}">
</app-video-item>
</li>
</ul>
<button *ngIf="showLoadMore()" mat-button [disabled]="loading" class="width100 mt-20" style="text-align: center;" (click)="loadMore()">
<i ofbicon *ngIf="loading">access_time</i><span *ngIf="loading"> Loading...</span>
<i ofbicon *ngIf="!loading">expand_more</i><span *ngIf="!loading"> Load More</span>
</button>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { YoutubeListComponent } from './youtube-list.component';
describe('YoutubeListComponent', () => {
let component: YoutubeListComponent;
let fixture: ComponentFixture<YoutubeListComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ YoutubeListComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(YoutubeListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,58 @@
import { Component, OnInit } from '@angular/core';
import { YoutubeListService } from './youtube-list-service';
import { take } from 'rxjs/operators';
import { YoutubeSnippet, YoutubeSnippetItem, YoutubeSearchResult } from './youtube-snippet';
@Component({
selector: 'app-youtube-list',
templateUrl: './youtube-list.component.html',
styleUrls: ['./youtube-list.component.css']
})
export class YoutubeListComponent implements OnInit {
public loading: boolean = false;
public items: YoutubeSnippetItem[] = [];
private totalResults: number = 0;
constructor(private youtube: YoutubeListService) { }
ngOnInit() {
this.loading = true;
this.youtube.getFirstPage().pipe(take(1)).subscribe(x => {
this.totalResults = x.pageInfo.totalResults;
this.processResults(x);
this.loading = false;
});
}
public showLoadMore() {
if (this.items.length < this.totalResults && this.loading === false) {
return true;
}
return false;
}
public loadMore() {
if (this.items.length >= this.totalResults) {
return;
}
this.loading = true;
this.youtube.getNextPage().pipe(take(1)).subscribe(x => {
this.processResults(x);
this.loading = false;
});
}
private processResults(res: YoutubeSearchResult) {
var parser = new DOMParser;
res.items.forEach(x => x.snippet.title = parser.parseFromString(x.snippet.title, "text/html").body.innerHTML);
if (this.items.length === 0) {
this.items = res.items;
} else {
for(let i = 0; i < res.items.length; i++) {
this.items.push(res.items[i]);
}
}
}
}

View File

@ -0,0 +1,48 @@
export class YoutubeSearchResult {
public kind: string;
public etag: string;
public nextPageToken: string;
public regionCode: string;
public pageInfo: YoutubeSearchResultPageInfo;
public items: YoutubeSnippetItem[];
}
export class YoutubeSearchResultPageInfo {
public totalResults: number;
public resultsPerPage: number;
}
export class YoutubeSnippetItem {
public kind: string;
public etag: string;
public id: YoutubeSnippetId;
public snippet: YoutubeSnippet
}
export class YoutubeSnippetId {
public kind: string;
public videoId: string;
}
export class YoutubeSnippet {
public publishedAt: Date;
public channelId: string;
public title: string;
public description: string;
public channelTitle: string;
public liveBroadcastContent: string;
public publishTime: Date;
public thumbnails: YoutubeSnippetThumbnails;
}
export class YoutubeSnippetThumbnails {
public default: YoutubeThumbnail;
public medium: YoutubeThumbnail;
public high: YoutubeThumbnail;
}
export class YoutubeThumbnail {
public url: string;
public width: number;
public height: number;
}

View File

@ -4,15 +4,17 @@ export const EVENTS_ADD_URL = environment.baseUrl + "/api2/events/a/";
export const EVENT_BY_ID = environment.baseUrl + "/api2/events/";
export const EVENTS_BY_PAGE_URL = environment.baseUrl + "/api2/events/page/";
export const EVENTS_DELETE_BY_ID_URL = environment.baseUrl + "/api2/events/a/";
export const MISSIONARY_SUPPORT_CREATE_URL = environment.baseApi + "/missionary";
export const SERMONS_BY_ID = environment.baseUrl + '/api2/sermons/';
export const SERMONS_BY_PAGE_URL = environment.baseUrl + '/api2/sermons/page/';
export const SERMONS_BY_SEARCH_URL = environment.baseUrl + '/api2/sermons/search';
export const SERMON_MP3_BASE_URL = '//ofbbutte.com/static/media/';
export const SERMON_MP3_BASE_URL = '/media/';
export const SERMON_ADD_URL = environment.baseUrl + "/api2/sermons/a/";
export const SERMON_DELETE_URL = environment.baseUrl + "/api2/sermons/a/";
export const SERMON_UPDATE_URL = environment.baseUrl + "/api2/sermons/a/";
export const SERMON_DOWNLOAD_URL = environment.baseUrl + "/api2/sermons/download/";
export const TRANSACTION_CREATE_URL = environment.baseUrl + "/api2/transactions/a/";
export const TRANSACTION_GET_BY_YEAR_URL = environment.baseUrl + "/api2/transactions/a/";
export const USER_CREATE_URL = environment.baseUrl + "/api2/users/a/";
export const USER_GET_ALL_URL = environment.baseUrl + "/api2/users/a";
export const LOGIN_URL = environment.baseUrl + '/api2/login';

View File

@ -1,6 +1,7 @@
export const ICONS = {
access_time: '&#xE192;',
add: '&#xE145;',
announcement: '&#xE85A;',
close: '&#xE5CD;',
copyright: '&#xE90C;',
delete_forever: '&#xE92B;',

View File

@ -10,6 +10,9 @@ export class OfbDatePipe implements PipeTransform {
console.error("value for ofb-date-pipe was undefined or null");
return value;
}
if (typeof value === 'string') {
value = new Date(value);
}
var year = value.getFullYear();
var month = value.getMonth() + 1;
var day = value.getDate();

View File

@ -0,0 +1,41 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { MISSIONARY_SUPPORT_CREATE_URL } from '../constants/urls';
@Injectable()
export class MissionarySupportService {
private options: {};
constructor(private httpClient: HttpClient){
this.options = {
withCredentials: true
};
}
create(body: any) {
console.log(body);
return this.httpClient.post(MISSIONARY_SUPPORT_CREATE_URL, body, this.options)
.pipe(tap(res => console.log(res)), catchError(this.handleError));
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
console.error(error);
}
// return an observable with a user-facing error message
return throwError((error && error.error && error.error.message) ? error.error.message :
'Something bad happened; please try again later.');
};
}

View File

@ -0,0 +1,15 @@
import { Injectable } from '@angular/core';
@Injectable()
export class PrintService {
public isPrinting: boolean = false;
constructor(){}
public setPrinting(printing: boolean) {
this.isPrinting = printing;
}
}

View File

@ -4,7 +4,7 @@ import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, Subject, throwError, of } from 'rxjs';
import { catchError, map, tap, first } from 'rxjs/operators';
import { TRANSACTION_CREATE_URL } from '../constants/urls';
import { TRANSACTION_CREATE_URL, TRANSACTION_GET_BY_YEAR_URL } from '../constants/urls';
import { Transaction } from '../components/add-transaction-page/transaction';
@Injectable()
@ -41,6 +41,11 @@ export class TransactionService {
.pipe(tap(res => console.log(res)), catchError(this.handleError));
}
getByYear(taxYear: number) {
return this.httpClient.get(TRANSACTION_GET_BY_YEAR_URL + "?taxYear=" + taxYear, this.options)
.pipe(catchError(this.handleError));
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.

View File

@ -19,7 +19,7 @@ export class UserService {
getAll(){
return this.httpClient.get(USER_GET_ALL_URL, this.options)
.pipe(tap(res => console.log(res)), catchError(this.handleError));
.pipe(catchError(this.handleError));
}
create(firstName: string, lastName: string, street: string, city: string, state: string, zip: string, country: string) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Some files were not shown because too many files have changed in this diff Show More