• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Code-4-Community / speak-for-the-trees-frontend / 5412403313

pending completion
5412403313

Pull #258

github

web-flow
Merge 880fa4481 into 14ce0a457
Pull Request #258: Ah.prevent open adoption

228 of 839 branches covered (27.18%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

1041 of 2251 relevant lines covered (46.25%)

16.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.23
/src/components/treeInfo/index.tsx
1
import React from 'react';
2
import { useHistory, useLocation } from 'react-router-dom';
3
import { Typography, Button, FormInstance } from 'antd';
4
import { RedirectStateProps, Routes } from '../../App';
5
import StewardshipForm from '../forms/stewardshipForm';
6
import styled from 'styled-components';
7
import { useSelector } from 'react-redux';
8
import { SiteProps } from '../../containers/treePage/ducks/types';
9
import {
10
  NameSiteEntryRequest,
11
  RecordStewardshipRequest,
12
} from '../forms/ducks/types';
13
import { MID_GREEN } from '../../utils/colors';
14
import ShareButton from '../../components/shareButton';
15
import TreePageHeader from '../treePageHeader';
16
import { C4CState } from '../../store';
17
import { isAdmin } from '../../auth/ducks/selectors';
18
import { useTranslation } from 'react-i18next';
19
import { site } from '../../constants';
20
import { n } from '../../utils/stringFormat';
21
import { isSFTT } from '../../utils/isCheck';
22
import { getCommonName } from '../../utils/treeFunctions';
23

24
const TreeHeader = styled.div`
12✔
25
  text-transform: capitalize;
26
`;
27

28
const StewardshipContainer = styled.div`
12✔
29
  margin-top: 40px;
30
`;
31

32
const ToggleTextButton = styled(Button)`
12✔
33
  padding: 0px;
34
`;
35

36
const UnadoptButton = styled(Button)`
12✔
37
  & :hover {
38
    background-color: #fff1f1;
39
  }
40
`;
41

42
const ForceUnadoptButton = styled(Button)`
12✔
43
  margin: 10px;
44
`;
45

46
interface TreeProps {
47
  readonly siteData: SiteProps;
48
  readonly loggedIn: boolean;
49
  readonly userOwnsTree: boolean;
50
  readonly mobile?: boolean;
51
  readonly onClickAdopt: () => void;
52
  readonly onClickUnadopt: () => void;
53
  readonly onClickForceUnadopt: () => void;
54
  readonly onFinishRecordStewardship: (
55
    values: RecordStewardshipRequest,
56
  ) => void;
57
  readonly stewardshipFormInstance: FormInstance;
58
  readonly editTreeNameFormInstance: FormInstance<NameSiteEntryRequest>;
59
  readonly onClickEditTreeName: (values: NameSiteEntryRequest) => void;
60
}
61

62
const TreeInfo: React.FC<TreeProps> = ({
12✔
63
  siteData,
64
  loggedIn,
65
  userOwnsTree,
66
  mobile,
67
  onClickAdopt,
68
  onClickUnadopt,
69
  onClickForceUnadopt,
70
  onFinishRecordStewardship,
71
  stewardshipFormInstance,
72
  editTreeNameFormInstance,
73
  onClickEditTreeName,
74
}) => {
75
  const { t } = useTranslation(n(site, ['treeInfo']), { nsMode: 'fallback' });
×
76

77
  const history = useHistory();
×
78
  const location = useLocation<RedirectStateProps>();
×
79

80
  const isAdopted = !!siteData.entries?.[0]?.adopter;
×
81
  const treePresent = siteData.entries[0].treePresent;
×
82

83
  const treeCommonName = getCommonName(siteData);
×
84

85
  const userIsAdmin: boolean = useSelector((state: C4CState) =>
×
86
    isAdmin(state.authenticationState.tokens),
×
87
  );
88

89
  const getSiteLocation = (): string => {
×
90
    // TODO change to siteData.city and remove check for zip after data is cleaned
91
    let baseLocation = isSFTT() ? 'Boston' : 'Cambridge';
×
92
    if (siteData.zip) {
×
93
      baseLocation += ` ${siteData.zip}`;
×
94
    }
95

96
    if (siteData.address) {
×
97
      return `${siteData.address}, ${baseLocation}`;
×
98
    } else {
99
      return baseLocation;
×
100
    }
101
  };
102

103
  const shareButton = (
104
    <ShareButton
×
105
      size={mobile ? 'middle' : 'large'}
×
106
      defaultText={
107
        userOwnsTree
×
108
          ? t('share_messages.user_owns', { treeCommonName })
109
          : isAdopted
×
110
          ? t('share_messages.adopted', { treeCommonName })
111
          : t('share_messages.open', {
112
              treeCommonName,
113
              location: siteData.address ? `at ${siteData.address}` : '',
×
114
            })
115
      }
116
      link={`map.treeboston.org/tree/${siteData.siteId}`}
117
    />
118
  );
119

120
  return (
×
121
    <>
122
      <TreeHeader>
123
        {
124
          <TreePageHeader
125
            // Display 'Open Planting Site' if no tree has been planted
126
            // Otherwise, display the tree's commonName or 'Unknown Species' if no commonName exists
127
            pageTitle={
128
              siteData.entries[0]?.treePresent
×
129
                ? siteData.entries[0].commonName
×
130
                  ? siteData.entries[0].commonName
131
                  : t('species_title.unknown')
132
                : t('species_title.open')
133
            }
134
            pageSubtitle={getSiteLocation()}
135
            isMobile={mobile}
136
            canEditTreeName={userIsAdmin || userOwnsTree}
×
137
            subtitlecolor={MID_GREEN}
138
            editTreeNameForm={editTreeNameFormInstance}
139
            onClickEditTreeName={onClickEditTreeName}
140
            treeName={siteData.entries[0].treeName || ''}
×
141
          />
142
        }
143
      </TreeHeader>
144

145
      {loggedIn ? (
×
146
        <>
147
          {treePresent && (
×
148
            <>
149
              {userOwnsTree ? (
×
150
                <UnadoptButton
151
                  danger
152
                  size={mobile ? 'middle' : 'large'}
×
153
                  onClick={onClickUnadopt}
154
                >
155
                  {t('actions.unadopt')}
156
                </UnadoptButton>
157
              ) : (
158
                <Button
159
                  type="primary"
160
                  size={mobile ? 'middle' : 'large'}
×
161
                  onClick={onClickAdopt}
162
                  disabled={isAdopted}
163
                >
164
                  {isAdopted ? t('actions.adopted') : t('actions.adopt')}
×
165
                </Button>
166
              )}
167

168
              {userIsAdmin && (
×
169
                <ForceUnadoptButton
170
                  danger
171
                  size={mobile ? 'middle' : 'large'}
×
172
                  onClick={onClickForceUnadopt}
173
                  disabled={!isAdopted}
174
                >
175
                  {t('actions.force_unadopt')}
176
                </ForceUnadoptButton>
177
              )}
178
            </>
179
          )}
180

181
          {shareButton}
182

183
          {userOwnsTree && treePresent && (
×
184
            <StewardshipContainer>
185
              <Typography.Title level={3}>
186
                {t('actions.record_activity')}
187
              </Typography.Title>
188
              <StewardshipForm
189
                onFinish={onFinishRecordStewardship}
190
                form={stewardshipFormInstance}
191
              />
192
            </StewardshipContainer>
193
          )}
194
        </>
195
      ) : (
196
        <>
197
          <ToggleTextButton
198
            type="link"
199
            size="large"
200
            onClick={() =>
201
              history.push(Routes.LOGIN, {
×
202
                destination: location.pathname,
203
              })
204
            }
205
          >
206
            {t('log_in')}
207
          </ToggleTextButton>
208
          {shareButton}
209
        </>
210
      )}
211
    </>
212
  );
213
};
214

215
export default TreeInfo;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc